neoagent 2.5.2-beta.2 → 2.5.2-beta.3
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/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
abe2552b23bc51626fa18b7baf5d91d0
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"77e2e94772b6eb43759e34ed1ad7da4674e19c
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "946389838" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -134794,7 +134794,7 @@ r===$&&A.b()
|
|
|
134794
134794
|
p.push(A.jP(q,A.j9(!1,new A.a_(B.uG,A.d8(new A.cA(B.jt,new A.a7N(r,q),q),q,q),q),!1,B.H,!0),q,q,0,0,0,q))}r=!1
|
|
134795
134795
|
if(!s.ay)if(!s.ch){r=s.e
|
|
134796
134796
|
r===$&&A.b()
|
|
134797
|
-
r=B.b.u("
|
|
134797
|
+
r=B.b.u("mqex3krf-3096c64").length!==0&&r.b}if(r){r=s.d
|
|
134798
134798
|
r===$&&A.b()
|
|
134799
134799
|
r=r.aP&&!r.ai?84:0
|
|
134800
134800
|
s=s.e
|
|
@@ -140506,7 +140506,7 @@ $S:0}
|
|
|
140506
140506
|
A.a_6.prototype={}
|
|
140507
140507
|
A.SQ.prototype={
|
|
140508
140508
|
nb(a){var s=this
|
|
140509
|
-
if(B.b.u("
|
|
140509
|
+
if(B.b.u("mqex3krf-3096c64").length===0||s.a!=null)return
|
|
140510
140510
|
s.AU()
|
|
140511
140511
|
s.a=A.on(B.RH,new A.bc8(s))},
|
|
140512
140512
|
AU(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
|
|
@@ -140524,7 +140524,7 @@ if(!t.f.b(k)){s=1
|
|
|
140524
140524
|
break}i=J.a3(k,"buildId")
|
|
140525
140525
|
h=i==null?null:B.b.u(J.p(i))
|
|
140526
140526
|
j=h==null?"":h
|
|
140527
|
-
if(J.bi(j)===0||J.d(j,"
|
|
140527
|
+
if(J.bi(j)===0||J.d(j,"mqex3krf-3096c64")){s=1
|
|
140528
140528
|
break}n.b=!0
|
|
140529
140529
|
n.F()
|
|
140530
140530
|
p=2
|
|
@@ -140541,7 +140541,7 @@ case 2:return A.i(o.at(-1),r)}})
|
|
|
140541
140541
|
return A.k($async$AU,r)},
|
|
140542
140542
|
vE(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1
|
|
140543
140543
|
var $async$vE=A.h(function(a2,a3){if(a2===1){o.push(a3)
|
|
140544
|
-
s=p}for(;;)switch(s){case 0:if(B.b.u("
|
|
140544
|
+
s=p}for(;;)switch(s){case 0:if(B.b.u("mqex3krf-3096c64").length===0||n.c){s=1
|
|
140545
140545
|
break}n.c=!0
|
|
140546
140546
|
n.F()
|
|
140547
140547
|
p=4
|
|
@@ -116,6 +116,7 @@ const MESSAGING_PROGRESS_FIRST_UPDATE_MS = 60 * 1000;
|
|
|
116
116
|
const MESSAGING_PROGRESS_REPEAT_MS = 90 * 1000;
|
|
117
117
|
const MESSAGING_PROGRESS_STALL_MS = 240 * 1000;
|
|
118
118
|
const MESSAGING_PROGRESS_TICK_MS = 15 * 1000;
|
|
119
|
+
const GOAL_CONTRACT_SUCCESS_CRITERIA_LIMIT = 12;
|
|
119
120
|
|
|
120
121
|
function isoNow() {
|
|
121
122
|
return new Date().toISOString();
|
|
@@ -186,6 +187,302 @@ function hasVisibleInterimActivity(runMeta) {
|
|
|
186
187
|
);
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
function normalizeGoalCriteria(value) {
|
|
191
|
+
if (!Array.isArray(value)) return [];
|
|
192
|
+
const seen = new Set();
|
|
193
|
+
const items = [];
|
|
194
|
+
for (const entry of value) {
|
|
195
|
+
const text = String(entry || '').trim();
|
|
196
|
+
if (!text) continue;
|
|
197
|
+
const signature = text.toLowerCase();
|
|
198
|
+
if (seen.has(signature)) continue;
|
|
199
|
+
seen.add(signature);
|
|
200
|
+
items.push(text);
|
|
201
|
+
if (items.length >= GOAL_CONTRACT_SUCCESS_CRITERIA_LIMIT) break;
|
|
202
|
+
}
|
|
203
|
+
return items;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeGoalContract(raw = null) {
|
|
207
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
208
|
+
const goal = String(raw.goal || '').trim();
|
|
209
|
+
const successCriteria = normalizeGoalCriteria(
|
|
210
|
+
raw.successCriteria || raw.success_criteria || [],
|
|
211
|
+
);
|
|
212
|
+
const rawCompletionConfidence = String(
|
|
213
|
+
raw.completionConfidenceRequired || raw.completion_confidence_required || '',
|
|
214
|
+
).trim();
|
|
215
|
+
const completionConfidenceRequired = rawCompletionConfidence
|
|
216
|
+
? normalizeCompletionConfidence(rawCompletionConfidence)
|
|
217
|
+
: '';
|
|
218
|
+
const progressUpdatePolicy = ['none', 'optional', 'required'].includes(String(
|
|
219
|
+
raw.progressUpdatePolicy || raw.progress_update_policy || '',
|
|
220
|
+
).trim().toLowerCase())
|
|
221
|
+
? String(raw.progressUpdatePolicy || raw.progress_update_policy || '').trim().toLowerCase()
|
|
222
|
+
: '';
|
|
223
|
+
const autonomyLevel = ['minimal', 'normal', 'high'].includes(String(
|
|
224
|
+
raw.autonomyLevel || raw.autonomy_level || '',
|
|
225
|
+
).trim().toLowerCase())
|
|
226
|
+
? String(raw.autonomyLevel || raw.autonomy_level || '').trim().toLowerCase()
|
|
227
|
+
: '';
|
|
228
|
+
const complexity = ['simple', 'standard', 'complex'].includes(String(
|
|
229
|
+
raw.complexity || '',
|
|
230
|
+
).trim().toLowerCase())
|
|
231
|
+
? String(raw.complexity || '').trim().toLowerCase()
|
|
232
|
+
: '';
|
|
233
|
+
|
|
234
|
+
if (
|
|
235
|
+
!goal
|
|
236
|
+
&& successCriteria.length === 0
|
|
237
|
+
&& !completionConfidenceRequired
|
|
238
|
+
&& !progressUpdatePolicy
|
|
239
|
+
&& !autonomyLevel
|
|
240
|
+
&& !complexity
|
|
241
|
+
) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
goal,
|
|
247
|
+
successCriteria,
|
|
248
|
+
completionConfidenceRequired,
|
|
249
|
+
progressUpdatePolicy: progressUpdatePolicy || '',
|
|
250
|
+
autonomyLevel: autonomyLevel || '',
|
|
251
|
+
complexity: complexity || '',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function mergeGoalContracts(existing = null, patch = null) {
|
|
256
|
+
const current = normalizeGoalContract(existing) || null;
|
|
257
|
+
const nextPatch = normalizeGoalContract(patch) || null;
|
|
258
|
+
if (!current && !nextPatch) return null;
|
|
259
|
+
|
|
260
|
+
const goal = String(nextPatch?.goal || current?.goal || '').trim();
|
|
261
|
+
const successCriteria = normalizeGoalCriteria([
|
|
262
|
+
...(current?.successCriteria || []),
|
|
263
|
+
...(nextPatch?.successCriteria || []),
|
|
264
|
+
]);
|
|
265
|
+
const completionConfidenceRequired = nextPatch?.completionConfidenceRequired
|
|
266
|
+
|| current?.completionConfidenceRequired
|
|
267
|
+
|| 'medium';
|
|
268
|
+
const progressUpdatePolicy = nextPatch?.progressUpdatePolicy
|
|
269
|
+
|| current?.progressUpdatePolicy
|
|
270
|
+
|| '';
|
|
271
|
+
const autonomyLevel = nextPatch?.autonomyLevel
|
|
272
|
+
|| current?.autonomyLevel
|
|
273
|
+
|| '';
|
|
274
|
+
const complexity = nextPatch?.complexity
|
|
275
|
+
|| current?.complexity
|
|
276
|
+
|| '';
|
|
277
|
+
|
|
278
|
+
return normalizeGoalContract({
|
|
279
|
+
goal,
|
|
280
|
+
successCriteria,
|
|
281
|
+
completionConfidenceRequired,
|
|
282
|
+
progressUpdatePolicy,
|
|
283
|
+
autonomyLevel,
|
|
284
|
+
complexity,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function goalContractFromAnalysis(analysis = null) {
|
|
289
|
+
if (!analysis || typeof analysis !== 'object') return null;
|
|
290
|
+
return normalizeGoalContract({
|
|
291
|
+
goal: analysis.goal,
|
|
292
|
+
successCriteria: analysis.success_criteria,
|
|
293
|
+
completionConfidenceRequired: analysis.completion_confidence_required,
|
|
294
|
+
progressUpdatePolicy: analysis.progress_update_policy,
|
|
295
|
+
autonomyLevel: analysis.autonomy_level,
|
|
296
|
+
complexity: analysis.complexity,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function goalContractFromPlan(plan = null) {
|
|
301
|
+
if (!plan || typeof plan !== 'object') return null;
|
|
302
|
+
return normalizeGoalContract({
|
|
303
|
+
successCriteria: plan.success_criteria,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function buildResolvedGoalContract(runMeta, analysis = null, plan = null) {
|
|
308
|
+
let contract = mergeGoalContracts(runMeta?.goalContract || null, goalContractFromAnalysis(analysis));
|
|
309
|
+
contract = mergeGoalContracts(contract, goalContractFromPlan(plan));
|
|
310
|
+
return contract;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function buildGoalContractPrompt(contract, label = 'Persistent run goal') {
|
|
314
|
+
const normalized = normalizeGoalContract(contract);
|
|
315
|
+
if (!normalized) return '';
|
|
316
|
+
const lines = [];
|
|
317
|
+
if (normalized.goal) {
|
|
318
|
+
lines.push(`${label}: ${normalized.goal}`);
|
|
319
|
+
}
|
|
320
|
+
if (normalized.successCriteria.length > 0) {
|
|
321
|
+
lines.push(`Persistent success criteria:\n- ${normalized.successCriteria.join('\n- ')}`);
|
|
322
|
+
}
|
|
323
|
+
const contractLine = [
|
|
324
|
+
normalized.complexity ? `complexity=${normalized.complexity}` : '',
|
|
325
|
+
normalized.autonomyLevel ? `autonomy_level=${normalized.autonomyLevel}` : '',
|
|
326
|
+
normalized.progressUpdatePolicy ? `progress_update_policy=${normalized.progressUpdatePolicy}` : '',
|
|
327
|
+
normalized.completionConfidenceRequired ? `completion_confidence_required=${normalized.completionConfidenceRequired}` : '',
|
|
328
|
+
].filter(Boolean).join('; ');
|
|
329
|
+
if (contractLine) {
|
|
330
|
+
lines.push(`Persistent autonomy contract: ${contractLine}`);
|
|
331
|
+
}
|
|
332
|
+
return lines.join('\n');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function resolveRunGoalContext(runMeta, analysis = null, plan = null) {
|
|
336
|
+
const goalContract = buildResolvedGoalContract(runMeta, analysis, plan);
|
|
337
|
+
const successCriteria = goalContract?.successCriteria?.length
|
|
338
|
+
? goalContract.successCriteria.slice(0, 6)
|
|
339
|
+
: (Array.isArray(plan?.success_criteria)
|
|
340
|
+
? plan.success_criteria
|
|
341
|
+
.map((item) => String(item || '').trim())
|
|
342
|
+
.filter(Boolean)
|
|
343
|
+
.slice(0, 6)
|
|
344
|
+
: []);
|
|
345
|
+
const effectiveGoal = goalContract?.goal || analysis?.goal || '';
|
|
346
|
+
const effectiveComplexity = goalContract?.complexity || analysis?.complexity || 'standard';
|
|
347
|
+
const effectiveAutonomyLevel = goalContract?.autonomyLevel || analysis?.autonomy_level || 'normal';
|
|
348
|
+
const effectiveProgressPolicy = goalContract?.progressUpdatePolicy || analysis?.progress_update_policy || 'optional';
|
|
349
|
+
const effectiveCompletionConfidence = goalContract?.completionConfidenceRequired
|
|
350
|
+
|| analysis?.completion_confidence_required
|
|
351
|
+
|| 'medium';
|
|
352
|
+
const persistedGoalPrompt = buildGoalContractPrompt(goalContract);
|
|
353
|
+
return {
|
|
354
|
+
goalContract,
|
|
355
|
+
successCriteria,
|
|
356
|
+
effectiveGoal,
|
|
357
|
+
effectiveComplexity,
|
|
358
|
+
effectiveAutonomyLevel,
|
|
359
|
+
effectiveProgressPolicy,
|
|
360
|
+
effectiveCompletionConfidence,
|
|
361
|
+
persistedGoalPrompt,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function buildCompletionDecisionPrompt({
|
|
366
|
+
mode,
|
|
367
|
+
triggerSource,
|
|
368
|
+
messagingSent = false,
|
|
369
|
+
goalContext,
|
|
370
|
+
parallelWork = false,
|
|
371
|
+
tools,
|
|
372
|
+
toolExecutions,
|
|
373
|
+
lastReply,
|
|
374
|
+
iteration,
|
|
375
|
+
maxIterations,
|
|
376
|
+
progressSummary = '',
|
|
377
|
+
platform = null,
|
|
378
|
+
}) {
|
|
379
|
+
const draftReply = mode === 'messaging'
|
|
380
|
+
? (normalizeOutgoingMessage(lastReply || '', platform, { collapseWhitespace: false })
|
|
381
|
+
? String(lastReply || '').trim()
|
|
382
|
+
: '')
|
|
383
|
+
: normalizeOutgoingMessage(lastReply) || '';
|
|
384
|
+
const lines = [
|
|
385
|
+
'Return JSON only.',
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
if (mode === 'messaging') {
|
|
389
|
+
lines.push(
|
|
390
|
+
'A messaging run is about to stop after sending user-visible progress, but no final delivery has happened yet.',
|
|
391
|
+
'Decide whether the run should keep working, finish with the completed result now, or stop with one blocker reply now.',
|
|
392
|
+
'Schema: {"status":"continue|complete|blocked","reason":"short concrete reason","final_reply":"string"}',
|
|
393
|
+
'Rules:',
|
|
394
|
+
'- Use "continue" whenever any safe next step remains in this same run.',
|
|
395
|
+
'- Use "complete" only when the requested outcome is actually achieved and final_reply is the finished user-facing answer to send now.',
|
|
396
|
+
'- Use "blocked" only when a specific external dependency, missing user input, or permission outside this run is required and final_reply is the concise blocker reply to send now.',
|
|
397
|
+
'- A progress note, next-step note, apology, plan, or "I will investigate" draft is "continue", not "complete" and not "blocked".',
|
|
398
|
+
'- If user-visible progress was already sent and no final delivery exists yet, do not stop silently and do not stop on a status-only draft.',
|
|
399
|
+
'- final_reply must be empty when status is "continue".',
|
|
400
|
+
);
|
|
401
|
+
} else {
|
|
402
|
+
lines.push(
|
|
403
|
+
'Decide whether this run should continue autonomously or stop now.',
|
|
404
|
+
'Schema: {"status":"continue|complete|blocked","reason":"short concrete reason"}',
|
|
405
|
+
'Rules:',
|
|
406
|
+
'- Use "continue" whenever any safe next step remains in this same run.',
|
|
407
|
+
'- Use "complete" only when the requested outcome is actually achieved or a truthful final user reply is already ready now.',
|
|
408
|
+
'- Use "blocked" only when a specific external dependency outside this run is required.',
|
|
409
|
+
'- If the latest draft asks the user for a missing required value, confirmation, or choice needed to proceed, use "blocked" so the run waits instead of repeating the same ask.',
|
|
410
|
+
'- A progress update is not complete.',
|
|
411
|
+
'- A single failed tool attempt is not blocked if another safe retry, verification step, or alternative path remains.',
|
|
412
|
+
'- A tool-specific API error, timeout, rate limit, or missing result inside this run is usually "continue", not "blocked", if any other available tool could still make progress.',
|
|
413
|
+
`- If completion_confidence_required is ${goalContext.effectiveCompletionConfidence} and the latest draft depends on unverified assumptions, use "continue" so the run can gather evidence, inspect state, or narrow the reply.`,
|
|
414
|
+
triggerSource === 'messaging' && messagingSent
|
|
415
|
+
? '- A reply was already delivered to the user via send_message. Use "complete" unless there is concrete remaining work (e.g., a tool call you still need to make) before the task is truly done. Do not send follow-up elaborations or re-introductions.'
|
|
416
|
+
: triggerSource === 'messaging'
|
|
417
|
+
? '- For messaging, do not stop on a partial status message. Continue unless the task is actually complete or externally blocked. If you already asked for missing user input, choose "blocked" and wait.'
|
|
418
|
+
: '- Do not stop just because you wrote a status update. Continue unless the task is actually complete or externally blocked.',
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
lines.push(
|
|
423
|
+
goalContext.effectiveGoal ? `Goal: ${goalContext.effectiveGoal}` : '',
|
|
424
|
+
goalContext.persistedGoalPrompt,
|
|
425
|
+
`Autonomy contract: complexity=${goalContext.effectiveComplexity}; autonomy_level=${goalContext.effectiveAutonomyLevel}; progress_update_policy=${goalContext.effectiveProgressPolicy}; parallel_work=${parallelWork === true}; completion_confidence_required=${goalContext.effectiveCompletionConfidence}.`,
|
|
426
|
+
goalContext.successCriteria.length > 0
|
|
427
|
+
? `Success criteria:\n${goalContext.successCriteria.map((item, index) => `${index + 1}. ${item}`).join('\n')}`
|
|
428
|
+
: '',
|
|
429
|
+
`Current iteration: ${iteration} of ${maxIterations}.`,
|
|
430
|
+
`Available tools in this run: ${summarizeAvailableTools(tools) || 'none'}`,
|
|
431
|
+
mode === 'messaging' && progressSummary ? `Progress ledger: ${progressSummary}` : '',
|
|
432
|
+
`Recent tool evidence:\n${summarizeToolExecutions(toolExecutions, 8) || 'none'}`,
|
|
433
|
+
`Latest draft reply:\n${draftReply || '(empty)'}`,
|
|
434
|
+
mode === 'messaging' ? buildPlatformFormattingGuide(platform) : '',
|
|
435
|
+
);
|
|
436
|
+
return lines.filter(Boolean).join('\n');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function normalizeCompletionDecision(raw, {
|
|
440
|
+
mode,
|
|
441
|
+
fallbackStatus = 'continue',
|
|
442
|
+
platform = null,
|
|
443
|
+
draftReply = '',
|
|
444
|
+
}) {
|
|
445
|
+
const allowed = new Set(['continue', 'complete', 'blocked']);
|
|
446
|
+
if (mode === 'messaging') {
|
|
447
|
+
let status = allowed.has(String(raw.status || '').trim().toLowerCase())
|
|
448
|
+
? String(raw.status || '').trim().toLowerCase()
|
|
449
|
+
: 'continue';
|
|
450
|
+
let finalReply = normalizeOutgoingMessage(raw.final_reply || '', platform, {
|
|
451
|
+
collapseWhitespace: false,
|
|
452
|
+
})
|
|
453
|
+
? String(raw.final_reply || '').trim()
|
|
454
|
+
: '';
|
|
455
|
+
if (status === 'continue') {
|
|
456
|
+
finalReply = '';
|
|
457
|
+
} else if (!finalReply && draftReply) {
|
|
458
|
+
finalReply = draftReply;
|
|
459
|
+
} else if (!finalReply) {
|
|
460
|
+
status = 'continue';
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
status,
|
|
464
|
+
reason: String(raw.reason || '').trim().slice(0, 400),
|
|
465
|
+
final_reply: finalReply,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const requestedStatus = String(raw.status || '').trim().toLowerCase();
|
|
470
|
+
return {
|
|
471
|
+
status: allowed.has(requestedStatus) ? requestedStatus : fallbackStatus,
|
|
472
|
+
reason: String(raw.reason || '').trim().slice(0, 400),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function shouldRequireMessagingFinalityCheck(runMeta) {
|
|
477
|
+
return Boolean(
|
|
478
|
+
runMeta
|
|
479
|
+
&& runMeta.triggerSource === 'messaging'
|
|
480
|
+
&& runMeta.finalDeliverySent !== true
|
|
481
|
+
&& !runMeta.terminalInterim
|
|
482
|
+
&& hasVisibleInterimActivity(runMeta)
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
189
486
|
function planningDepthForForceMode(forceMode) {
|
|
190
487
|
return forceMode === 'plan_execute' ? 'deep' : 'light';
|
|
191
488
|
}
|
|
@@ -629,6 +926,33 @@ class AgentEngine {
|
|
|
629
926
|
.run(JSON.stringify(next), runId);
|
|
630
927
|
}
|
|
631
928
|
|
|
929
|
+
replaceLatestConversationAssistantMessage(conversationId, content) {
|
|
930
|
+
if (!conversationId) return false;
|
|
931
|
+
const messageId = db.prepare(
|
|
932
|
+
`SELECT id
|
|
933
|
+
FROM conversation_messages
|
|
934
|
+
WHERE conversation_id = ? AND role = 'assistant'
|
|
935
|
+
ORDER BY id DESC
|
|
936
|
+
LIMIT 1`
|
|
937
|
+
).get(conversationId)?.id;
|
|
938
|
+
if (!messageId) return false;
|
|
939
|
+
db.prepare('UPDATE conversation_messages SET content = ? WHERE id = ?')
|
|
940
|
+
.run(content, messageId);
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
updateRunGoalContract(runId, patch = {}, options = {}) {
|
|
945
|
+
const runMeta = this.getRunMeta(runId);
|
|
946
|
+
if (!runMeta) return null;
|
|
947
|
+
runMeta.goalContract = mergeGoalContracts(runMeta.goalContract, patch);
|
|
948
|
+
if (options.persist !== false) {
|
|
949
|
+
this.persistRunMetadata(runId, {
|
|
950
|
+
goalContract: runMeta.goalContract,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
return runMeta.goalContract;
|
|
954
|
+
}
|
|
955
|
+
|
|
632
956
|
buildProgressLedgerSnapshot(runMeta) {
|
|
633
957
|
if (!runMeta?.progressLedger) return null;
|
|
634
958
|
return {
|
|
@@ -1152,53 +1476,31 @@ class AgentEngine {
|
|
|
1152
1476
|
options,
|
|
1153
1477
|
fallbackStatus,
|
|
1154
1478
|
}) {
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
.map((item) => String(item || '').trim())
|
|
1158
|
-
.filter(Boolean)
|
|
1159
|
-
.slice(0, 6)
|
|
1160
|
-
: [];
|
|
1479
|
+
const runMeta = options?.runId ? this.getRunMeta(options.runId) : null;
|
|
1480
|
+
const goalContext = resolveRunGoalContext(runMeta, analysis, plan);
|
|
1161
1481
|
|
|
1162
1482
|
const response = await this.requestStructuredJson({
|
|
1163
1483
|
provider,
|
|
1164
1484
|
providerName,
|
|
1165
1485
|
model,
|
|
1166
1486
|
messages,
|
|
1167
|
-
prompt:
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
'- If completion_confidence_required is high and the latest draft depends on unverified assumptions, use "continue" so the run can gather evidence, inspect state, or narrow the reply.',
|
|
1180
|
-
triggerSource === 'messaging' && messagingSent
|
|
1181
|
-
? '- A reply was already delivered to the user via send_message. Use "complete" unless there is concrete remaining work (e.g., a tool call you still need to make) before the task is truly done. Do not send follow-up elaborations or re-introductions.'
|
|
1182
|
-
: triggerSource === 'messaging'
|
|
1183
|
-
? '- For messaging, do not stop on a partial status message. Continue unless the task is actually complete or externally blocked. If you already asked for missing user input, choose "blocked" and wait.'
|
|
1184
|
-
: '- Do not stop just because you wrote a status update. Continue unless the task is actually complete or externally blocked.',
|
|
1185
|
-
analysis?.goal ? `Goal: ${analysis.goal}` : '',
|
|
1186
|
-
`Autonomy contract: complexity=${analysis?.complexity || 'standard'}; autonomy_level=${analysis?.autonomy_level || 'normal'}; progress_update_policy=${analysis?.progress_update_policy || 'optional'}; parallel_work=${analysis?.parallel_work === true}; completion_confidence_required=${analysis?.completion_confidence_required || 'medium'}.`,
|
|
1187
|
-
successCriteria.length > 0 ? `Success criteria:\n${successCriteria.map((item, index) => `${index + 1}. ${item}`).join('\n')}` : '',
|
|
1188
|
-
`Current iteration: ${iteration} of ${maxIterations}.`,
|
|
1189
|
-
`Available tools in this run: ${summarizeAvailableTools(tools) || 'none'}`,
|
|
1190
|
-
`Recent tool evidence:\n${summarizeToolExecutions(toolExecutions, 8) || 'none'}`,
|
|
1191
|
-
`Latest draft reply:\n${normalizeOutgoingMessage(lastReply) || '(empty)'}`,
|
|
1192
|
-
].filter(Boolean).join('\n'),
|
|
1487
|
+
prompt: buildCompletionDecisionPrompt({
|
|
1488
|
+
mode: 'loop',
|
|
1489
|
+
triggerSource,
|
|
1490
|
+
messagingSent,
|
|
1491
|
+
goalContext,
|
|
1492
|
+
parallelWork: analysis?.parallel_work === true,
|
|
1493
|
+
tools,
|
|
1494
|
+
toolExecutions,
|
|
1495
|
+
lastReply,
|
|
1496
|
+
iteration,
|
|
1497
|
+
maxIterations,
|
|
1498
|
+
}),
|
|
1193
1499
|
maxTokens: 320,
|
|
1194
|
-
normalize: (raw) => {
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
status: allowed.has(requestedStatus) ? requestedStatus : fallbackStatus,
|
|
1199
|
-
reason: String(raw.reason || '').trim().slice(0, 400),
|
|
1200
|
-
};
|
|
1201
|
-
},
|
|
1500
|
+
normalize: (raw) => normalizeCompletionDecision(raw, {
|
|
1501
|
+
mode: 'loop',
|
|
1502
|
+
fallbackStatus,
|
|
1503
|
+
}),
|
|
1202
1504
|
fallback: { status: fallbackStatus },
|
|
1203
1505
|
reasoningEffort: this.getReasoningEffort(providerName, options),
|
|
1204
1506
|
telemetry: options,
|
|
@@ -1825,23 +2127,192 @@ class AgentEngine {
|
|
|
1825
2127
|
return { messages, appliedCount: queued.length };
|
|
1826
2128
|
}
|
|
1827
2129
|
|
|
2130
|
+
async decideMessagingCompletionState({
|
|
2131
|
+
provider,
|
|
2132
|
+
providerName,
|
|
2133
|
+
model,
|
|
2134
|
+
messages,
|
|
2135
|
+
analysis,
|
|
2136
|
+
plan,
|
|
2137
|
+
tools,
|
|
2138
|
+
toolExecutions,
|
|
2139
|
+
lastReply,
|
|
2140
|
+
iteration,
|
|
2141
|
+
maxIterations,
|
|
2142
|
+
runId,
|
|
2143
|
+
options,
|
|
2144
|
+
}) {
|
|
2145
|
+
const runMeta = this.getRunMeta(runId);
|
|
2146
|
+
const goalContext = resolveRunGoalContext(runMeta, analysis, plan);
|
|
2147
|
+
const platform = options?.source || null;
|
|
2148
|
+
const normalizedDraft = normalizeOutgoingMessage(lastReply || '', platform, {
|
|
2149
|
+
collapseWhitespace: false,
|
|
2150
|
+
});
|
|
2151
|
+
const draftReply = normalizedDraft ? String(lastReply || '').trim() : '';
|
|
2152
|
+
const ledger = runMeta?.progressLedger || null;
|
|
2153
|
+
const progressSummary = [
|
|
2154
|
+
`progress_state=${ledger?.progressState || 'active'}`,
|
|
2155
|
+
`current_phase=${ledger?.currentPhase || 'idle'}`,
|
|
2156
|
+
`current_tool=${ledger?.currentTool || 'none'}`,
|
|
2157
|
+
`heartbeat_count=${Number(ledger?.heartbeatCount || 0)}`,
|
|
2158
|
+
`last_visible_update=${ledger?.lastUserVisibleUpdateAt || 'none'}`,
|
|
2159
|
+
`last_verified_progress=${ledger?.lastVerifiedProgressAt || 'none'}`,
|
|
2160
|
+
`last_final_delivery=${ledger?.lastFinalDeliveryAt || 'none'}`,
|
|
2161
|
+
].join('; ');
|
|
2162
|
+
|
|
2163
|
+
const response = await this.requestStructuredJson({
|
|
2164
|
+
provider,
|
|
2165
|
+
providerName,
|
|
2166
|
+
model,
|
|
2167
|
+
messages,
|
|
2168
|
+
prompt: buildCompletionDecisionPrompt({
|
|
2169
|
+
mode: 'messaging',
|
|
2170
|
+
goalContext,
|
|
2171
|
+
parallelWork: analysis?.parallel_work === true,
|
|
2172
|
+
tools,
|
|
2173
|
+
toolExecutions,
|
|
2174
|
+
lastReply: draftReply,
|
|
2175
|
+
iteration,
|
|
2176
|
+
maxIterations,
|
|
2177
|
+
progressSummary,
|
|
2178
|
+
platform,
|
|
2179
|
+
}),
|
|
2180
|
+
maxTokens: 480,
|
|
2181
|
+
normalize: (raw) => normalizeCompletionDecision(raw, {
|
|
2182
|
+
mode: 'messaging',
|
|
2183
|
+
platform,
|
|
2184
|
+
draftReply,
|
|
2185
|
+
}),
|
|
2186
|
+
fallback: {
|
|
2187
|
+
status: 'continue',
|
|
2188
|
+
reason: '',
|
|
2189
|
+
final_reply: '',
|
|
2190
|
+
},
|
|
2191
|
+
reasoningEffort: this.getReasoningEffort(providerName, options),
|
|
2192
|
+
telemetry: options,
|
|
2193
|
+
phase: 'messaging_completion',
|
|
2194
|
+
});
|
|
2195
|
+
|
|
2196
|
+
return {
|
|
2197
|
+
decision: response.value,
|
|
2198
|
+
usage: response.usage,
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
async resolveMessagingCompletionDecision({
|
|
2203
|
+
provider,
|
|
2204
|
+
providerName,
|
|
2205
|
+
model,
|
|
2206
|
+
messages,
|
|
2207
|
+
analysis,
|
|
2208
|
+
plan,
|
|
2209
|
+
tools,
|
|
2210
|
+
toolExecutions,
|
|
2211
|
+
lastReply,
|
|
2212
|
+
iteration,
|
|
2213
|
+
maxIterations,
|
|
2214
|
+
runId,
|
|
2215
|
+
conversationId,
|
|
2216
|
+
options,
|
|
2217
|
+
}) {
|
|
2218
|
+
const runMeta = this.getRunMeta(runId);
|
|
2219
|
+
if (!shouldRequireMessagingFinalityCheck(runMeta)) {
|
|
2220
|
+
return {
|
|
2221
|
+
action: 'none',
|
|
2222
|
+
content: lastReply,
|
|
2223
|
+
reason: '',
|
|
2224
|
+
usage: 0,
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
let completionDecision;
|
|
2229
|
+
try {
|
|
2230
|
+
completionDecision = await this.decideMessagingCompletionState({
|
|
2231
|
+
provider,
|
|
2232
|
+
providerName,
|
|
2233
|
+
model,
|
|
2234
|
+
messages,
|
|
2235
|
+
analysis,
|
|
2236
|
+
plan,
|
|
2237
|
+
tools,
|
|
2238
|
+
toolExecutions,
|
|
2239
|
+
lastReply,
|
|
2240
|
+
iteration,
|
|
2241
|
+
maxIterations,
|
|
2242
|
+
runId,
|
|
2243
|
+
options,
|
|
2244
|
+
});
|
|
2245
|
+
} catch (error) {
|
|
2246
|
+
if (iteration >= maxIterations) {
|
|
2247
|
+
const wrapped = new Error(
|
|
2248
|
+
`Messaging completion check failed after visible progress: ${error?.message || error}`,
|
|
2249
|
+
);
|
|
2250
|
+
wrapped.disableAutonomousRetry = error?.disableAutonomousRetry === true;
|
|
2251
|
+
throw wrapped;
|
|
2252
|
+
}
|
|
2253
|
+
return {
|
|
2254
|
+
action: 'continue',
|
|
2255
|
+
content: '',
|
|
2256
|
+
reason: 'The run still needs an explicit final result or blocker decision.',
|
|
2257
|
+
usage: 0,
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
const decision = completionDecision.decision || { status: 'continue', reason: '' };
|
|
2262
|
+
if (decision.status === 'continue') {
|
|
2263
|
+
if (iteration >= maxIterations) {
|
|
2264
|
+
throw new Error(
|
|
2265
|
+
'Messaging run reached the iteration limit before producing a final answer or blocker after visible progress.',
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
return {
|
|
2269
|
+
action: 'continue',
|
|
2270
|
+
content: '',
|
|
2271
|
+
reason: decision.reason || 'The current draft is still only progress.',
|
|
2272
|
+
usage: completionDecision.usage || 0,
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
const finalContent = String(decision.final_reply || lastReply || '').trim();
|
|
2277
|
+
if (finalContent && messages[messages.length - 1]?.role === 'assistant') {
|
|
2278
|
+
messages[messages.length - 1] = {
|
|
2279
|
+
...messages[messages.length - 1],
|
|
2280
|
+
content: finalContent,
|
|
2281
|
+
};
|
|
2282
|
+
this.replaceLatestConversationAssistantMessage(conversationId, finalContent);
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
return {
|
|
2286
|
+
action: decision.status === 'blocked' ? 'blocked' : 'complete',
|
|
2287
|
+
content: finalContent,
|
|
2288
|
+
reason: decision.reason || '',
|
|
2289
|
+
usage: completionDecision.usage || 0,
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
|
|
1828
2293
|
buildMessagingHeartbeatText(runMeta, options = {}) {
|
|
1829
2294
|
const stalled = options.stalled === true;
|
|
1830
|
-
const
|
|
1831
|
-
const
|
|
2295
|
+
const now = Date.now();
|
|
2296
|
+
const runStartedAtMs = Number.isFinite(runMeta?.startedAt) ? runMeta.startedAt : now;
|
|
2297
|
+
const stepStartedAtMs = timestampMs(
|
|
1832
2298
|
runMeta?.progressLedger?.currentStepStartedAt,
|
|
1833
|
-
|
|
2299
|
+
0,
|
|
1834
2300
|
);
|
|
1835
|
-
const
|
|
2301
|
+
const runElapsed = formatElapsedDuration(now - runStartedAtMs);
|
|
2302
|
+
const stepElapsed = formatElapsedDuration(now - (stepStartedAtMs || runStartedAtMs));
|
|
2303
|
+
const unverifiedElapsed = formatElapsedDuration(now - timestampMs(
|
|
2304
|
+
runMeta?.progressLedger?.lastVerifiedProgressAt,
|
|
2305
|
+
runStartedAtMs,
|
|
2306
|
+
));
|
|
1836
2307
|
const currentTool = String(runMeta?.progressLedger?.currentTool || '').trim();
|
|
1837
2308
|
if (currentTool) {
|
|
1838
2309
|
return stalled
|
|
1839
|
-
? `Still working on ${currentTool}.
|
|
1840
|
-
: `Still working on ${currentTool}. ${
|
|
2310
|
+
? `Still working on ${currentTool}. Run active ${runElapsed}; no verified progress for ${unverifiedElapsed}.`
|
|
2311
|
+
: `Still working on ${currentTool}. Run active ${runElapsed}; current step ${stepElapsed} so far.`;
|
|
1841
2312
|
}
|
|
1842
2313
|
return stalled
|
|
1843
|
-
? `Still working on this.
|
|
1844
|
-
: `Still working on this. ${
|
|
2314
|
+
? `Still working on this. Run active ${runElapsed}; no verified progress for ${unverifiedElapsed}.`
|
|
2315
|
+
: `Still working on this. Run active ${runElapsed}.`;
|
|
1845
2316
|
}
|
|
1846
2317
|
|
|
1847
2318
|
async sendRuntimeMessagingHeartbeat(runId, options = {}) {
|
|
@@ -2317,6 +2788,7 @@ class AgentEngine {
|
|
|
2317
2788
|
const carriedExplicitMessageSent = retryMessagingState.explicitMessageSent === true;
|
|
2318
2789
|
const carriedInterimHistory = cloneInterimHistory(retryMessagingState.interimHistory);
|
|
2319
2790
|
const carriedLastInterimMessage = carriedInterimHistory[carriedInterimHistory.length - 1]?.content || '';
|
|
2791
|
+
const carriedGoalContract = normalizeGoalContract(retryMessagingState.goalContract);
|
|
2320
2792
|
const startedAtIso = isoNow();
|
|
2321
2793
|
const progressLedger = buildInitialProgressLedger({
|
|
2322
2794
|
startedAt: startedAtIso,
|
|
@@ -2358,10 +2830,12 @@ class AgentEngine {
|
|
|
2358
2830
|
chatId: options.chatId || null,
|
|
2359
2831
|
}
|
|
2360
2832
|
: null,
|
|
2833
|
+
goalContract: carriedGoalContract,
|
|
2361
2834
|
progressLedger,
|
|
2362
2835
|
});
|
|
2363
2836
|
this.persistRunMetadata(runId, {
|
|
2364
2837
|
progressLedger,
|
|
2838
|
+
goalContract: carriedGoalContract,
|
|
2365
2839
|
});
|
|
2366
2840
|
this.startMessagingProgressSupervisor(runId);
|
|
2367
2841
|
this.emit(userId, 'run:start', { runId, agentId, title: runTitle, model, triggerType, triggerSource });
|
|
@@ -2459,6 +2933,12 @@ class AgentEngine {
|
|
|
2459
2933
|
if (threadStateMessage) {
|
|
2460
2934
|
messages.push({ role: 'system', content: threadStateMessage });
|
|
2461
2935
|
}
|
|
2936
|
+
if (carriedGoalContract) {
|
|
2937
|
+
messages.push({
|
|
2938
|
+
role: 'system',
|
|
2939
|
+
content: buildGoalContractPrompt(carriedGoalContract, 'Persisted run goal'),
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2462
2942
|
this.recordRunEvent(userId, runId, 'memory_injected', {
|
|
2463
2943
|
hasRecallContext: Boolean(recallMsg),
|
|
2464
2944
|
hasThreadState: Boolean(threadStateMessage),
|
|
@@ -2537,6 +3017,7 @@ class AgentEngine {
|
|
|
2537
3017
|
taskAnalysis: analysis,
|
|
2538
3018
|
capabilityHealth,
|
|
2539
3019
|
});
|
|
3020
|
+
this.updateRunGoalContract(runId, goalContractFromAnalysis(analysis));
|
|
2540
3021
|
this.emit(userId, 'run:analysis', {
|
|
2541
3022
|
runId,
|
|
2542
3023
|
...analysis,
|
|
@@ -2655,6 +3136,9 @@ class AgentEngine {
|
|
|
2655
3136
|
plan: deliverablePlan,
|
|
2656
3137
|
},
|
|
2657
3138
|
});
|
|
3139
|
+
this.updateRunGoalContract(runId, {
|
|
3140
|
+
goal: deliverableWorkflow.selection.goal,
|
|
3141
|
+
});
|
|
2658
3142
|
this.recordRunEvent(userId, runId, 'deliverable_workflow_selected', {
|
|
2659
3143
|
type: deliverableWorkflow.selection.type,
|
|
2660
3144
|
confidence: deliverableWorkflow.selection.confidence,
|
|
@@ -2691,6 +3175,7 @@ class AgentEngine {
|
|
|
2691
3175
|
JSON.stringify(plan).slice(0, 20000)
|
|
2692
3176
|
);
|
|
2693
3177
|
this.persistRunMetadata(runId, { executionPlan: plan });
|
|
3178
|
+
this.updateRunGoalContract(runId, goalContractFromPlan(plan));
|
|
2694
3179
|
this.emit(userId, 'run:plan', {
|
|
2695
3180
|
runId,
|
|
2696
3181
|
steps: plan.steps,
|
|
@@ -2699,6 +3184,13 @@ class AgentEngine {
|
|
|
2699
3184
|
});
|
|
2700
3185
|
}
|
|
2701
3186
|
|
|
3187
|
+
const runGoalContract = this.getRunMeta(runId)?.goalContract || null;
|
|
3188
|
+
if (runGoalContract) {
|
|
3189
|
+
messages.push({
|
|
3190
|
+
role: 'system',
|
|
3191
|
+
content: buildGoalContractPrompt(runGoalContract, 'Run goal contract'),
|
|
3192
|
+
});
|
|
3193
|
+
}
|
|
2702
3194
|
messages.push({
|
|
2703
3195
|
role: 'system',
|
|
2704
3196
|
content: buildExecutionGuidance({
|
|
@@ -2954,6 +3446,43 @@ class AgentEngine {
|
|
|
2954
3446
|
})) {
|
|
2955
3447
|
break;
|
|
2956
3448
|
}
|
|
3449
|
+
const runMetaAfterResponse = this.getRunMeta(runId);
|
|
3450
|
+
if (shouldRequireMessagingFinalityCheck(runMetaAfterResponse)) {
|
|
3451
|
+
const messagingCompletion = await this.resolveMessagingCompletionDecision({
|
|
3452
|
+
provider,
|
|
3453
|
+
providerName,
|
|
3454
|
+
model,
|
|
3455
|
+
messages,
|
|
3456
|
+
analysis,
|
|
3457
|
+
plan,
|
|
3458
|
+
tools,
|
|
3459
|
+
toolExecutions,
|
|
3460
|
+
lastReply: lastContent,
|
|
3461
|
+
iteration,
|
|
3462
|
+
maxIterations,
|
|
3463
|
+
runId,
|
|
3464
|
+
conversationId,
|
|
3465
|
+
options: { ...options, runId, userId, agentId },
|
|
3466
|
+
});
|
|
3467
|
+
totalTokens += messagingCompletion.usage || 0;
|
|
3468
|
+
if (messagingCompletion.action === 'continue') {
|
|
3469
|
+
messages.push({
|
|
3470
|
+
role: 'system',
|
|
3471
|
+
content: [
|
|
3472
|
+
messagingCompletion.reason
|
|
3473
|
+
? `Continue working: ${messagingCompletion.reason}.`
|
|
3474
|
+
: 'Continue working autonomously.',
|
|
3475
|
+
'The messaging user has already seen progress. Do not stop until you either have the finished answer now or a concrete blocker reply now.',
|
|
3476
|
+
].join(' ')
|
|
3477
|
+
});
|
|
3478
|
+
lastContent = '';
|
|
3479
|
+
continue;
|
|
3480
|
+
}
|
|
3481
|
+
if (typeof messagingCompletion.content === 'string') {
|
|
3482
|
+
lastContent = messagingCompletion.content;
|
|
3483
|
+
}
|
|
3484
|
+
break;
|
|
3485
|
+
}
|
|
2957
3486
|
if (iteration < maxIterations) {
|
|
2958
3487
|
const proactiveRunNeedsDecision = (
|
|
2959
3488
|
(triggerSource === 'schedule' || triggerSource === 'tasks')
|
|
@@ -3784,6 +4313,10 @@ class AgentEngine {
|
|
|
3784
4313
|
...(Array.isArray(options?.messagingRetryState?.interimHistory) ? options.messagingRetryState.interimHistory : []),
|
|
3785
4314
|
...(Array.isArray(runMeta?.interimMessages) ? runMeta.interimMessages : []),
|
|
3786
4315
|
]),
|
|
4316
|
+
goalContract: mergeGoalContracts(
|
|
4317
|
+
options?.messagingRetryState?.goalContract || null,
|
|
4318
|
+
runMeta?.goalContract || null,
|
|
4319
|
+
),
|
|
3787
4320
|
lastUserVisibleUpdateAt: runMeta?.progressLedger?.lastUserVisibleUpdateAt || options?.messagingRetryState?.lastUserVisibleUpdateAt || null,
|
|
3788
4321
|
lastFinalDeliveryAt: runMeta?.progressLedger?.lastFinalDeliveryAt || options?.messagingRetryState?.lastFinalDeliveryAt || null,
|
|
3789
4322
|
heartbeatCount: Number(runMeta?.progressLedger?.heartbeatCount || options?.messagingRetryState?.heartbeatCount || 0),
|