@yeaft/webchat-agent 1.0.495 → 1.0.497
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/bin/yeaft-repo.js +4 -0
- package/local-runtime/version.json +1 -1
- package/package.json +3 -1
- package/repo-workflow-cli.js +101 -0
- package/repo-workflow.js +1437 -0
- package/skills/review-merge-tag/SKILL.md +78 -0
- package/yeaft/cli-session-runner.js +4 -1
- package/yeaft/engine.js +2 -0
- package/yeaft/init.js +5 -2
- package/yeaft/routing/router.js +140 -2
- package/yeaft/skills.js +58 -37
- package/yeaft/sub-agent/execution-control.js +90 -0
- package/yeaft/sub-agent/liveness.js +7 -0
- package/yeaft/sub-agent/runner.js +65 -13
- package/yeaft/sub-agent/spawned-prompt.js +20 -4
- package/yeaft/tools/activation.js +3 -0
- package/yeaft/tools/agent.js +27 -16
- package/yeaft/tools/index.js +2 -0
- package/yeaft/tools/repo-workflow.js +80 -0
- package/yeaft/tools/route-forward.js +19 -1
- package/yeaft/web-bridge.js +65 -7
package/yeaft/web-bridge.js
CHANGED
|
@@ -76,7 +76,12 @@ import { hydrateYeaftStatusFromSession } from './status-cache.js';
|
|
|
76
76
|
import { handleVpSubscribe } from './vp/vp-bridge.js';
|
|
77
77
|
import { createVp, updateVp, deleteVp, readVp, VpCrudError } from './vp/vp-crud.js';
|
|
78
78
|
import { scanVpLibrary } from './vp/vp-store.js';
|
|
79
|
-
import {
|
|
79
|
+
import {
|
|
80
|
+
bindRepoApprovalCapability,
|
|
81
|
+
createRouter,
|
|
82
|
+
isRepoApprovalCapability,
|
|
83
|
+
revokeRepoApprovalCapability,
|
|
84
|
+
} from './routing/router.js';
|
|
80
85
|
import {
|
|
81
86
|
SessionCrudError,
|
|
82
87
|
createSessionFromSpec,
|
|
@@ -462,6 +467,21 @@ const turnAbortCtrls = new Map();
|
|
|
462
467
|
* @type {Map<string, { sessionId: string, vpId: string, threadId: string, key: string }>}
|
|
463
468
|
*/
|
|
464
469
|
const turnAbortMeta = new Map();
|
|
470
|
+
/** @type {Map<string, { capability: object, sessionId: string, vpId: string }>} Exact-turn repo approvals, never persisted or projected. */
|
|
471
|
+
const turnRepoApprovals = new Map();
|
|
472
|
+
|
|
473
|
+
function revokeTurnRepoApproval(turnId) {
|
|
474
|
+
const grant = turnRepoApprovals.get(turnId);
|
|
475
|
+
turnRepoApprovals.delete(turnId);
|
|
476
|
+
return grant ? revokeRepoApprovalCapability(grant.capability) : false;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function revokeRepoApprovalsForRecipient(sessionId, vpId) {
|
|
480
|
+
for (const [turnId, grant] of turnRepoApprovals.entries()) {
|
|
481
|
+
if (grant.sessionId !== sessionId || grant.vpId !== vpId) continue;
|
|
482
|
+
revokeTurnRepoApproval(turnId);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
465
485
|
|
|
466
486
|
/**
|
|
467
487
|
* Per-VP status broker — the agent-side authority for VP timeline
|
|
@@ -1168,6 +1188,7 @@ function invalidateGroupContext(sessionId, { abortRuntime = false } = {}) {
|
|
|
1168
1188
|
}
|
|
1169
1189
|
for (const [turnId, meta] of Array.from(turnAbortMeta.entries())) {
|
|
1170
1190
|
if (meta?.sessionId !== sessionId) continue;
|
|
1191
|
+
revokeTurnRepoApproval(turnId);
|
|
1171
1192
|
turnAbortCtrls.delete(turnId);
|
|
1172
1193
|
turnAbortMeta.delete(turnId);
|
|
1173
1194
|
}
|
|
@@ -2001,7 +2022,9 @@ function getOrCreateSessionContext(sessionId, sessionHandle) {
|
|
|
2001
2022
|
const coord = createCoordinator(sessionHandle, {
|
|
2002
2023
|
deliver: (vpId, envelope) => enqueueForVp(sessionId, vpId, envelope),
|
|
2003
2024
|
});
|
|
2004
|
-
const router = createRouter({
|
|
2025
|
+
const router = createRouter({
|
|
2026
|
+
coordinator: coord,
|
|
2027
|
+
});
|
|
2005
2028
|
if (!entry) {
|
|
2006
2029
|
entry = makeGroupContextStub();
|
|
2007
2030
|
sessionContexts.set(sessionId, entry);
|
|
@@ -2138,12 +2161,21 @@ async function routeEnvelopeToVpThread(sessionId, vpId, envelope) {
|
|
|
2138
2161
|
let related = false;
|
|
2139
2162
|
|
|
2140
2163
|
const meta = envelope?.msg?.meta || {};
|
|
2164
|
+
const repoApproval = envelope?._repoApproval;
|
|
2165
|
+
const hasRepoApproval = isRepoApprovalCapability(repoApproval);
|
|
2166
|
+
if (envelope?.msg?.role === 'user' || envelope?.msg?.from === 'user') {
|
|
2167
|
+
revokeRepoApprovalsForRecipient(sessionId, vpId);
|
|
2168
|
+
}
|
|
2141
2169
|
const isTaskResult = meta.injectedBy === 'task_result';
|
|
2142
2170
|
const sourceThreadId = typeof meta.sourceThreadId === 'string' && meta.sourceThreadId.trim()
|
|
2143
2171
|
? meta.sourceThreadId.trim()
|
|
2144
2172
|
: null;
|
|
2145
2173
|
|
|
2146
|
-
if (
|
|
2174
|
+
if (hasRepoApproval) {
|
|
2175
|
+
// Approval is an execution capability, not text. It must never collapse
|
|
2176
|
+
// into a related thread's pendingQueries/rescue path.
|
|
2177
|
+
thread = getOrCreateVpThread({ sessionId, vpId, title: fallbackTitle(text) });
|
|
2178
|
+
} else if (isTaskResult && sourceThreadId) {
|
|
2147
2179
|
thread = getOrCreateVpThread({ sessionId, vpId, threadId: sourceThreadId, title: fallbackTitle(text) });
|
|
2148
2180
|
} else if (runningThreads.length === 0) {
|
|
2149
2181
|
thread = getOrCreateVpThread({ sessionId, vpId, title: fallbackTitle(text) });
|
|
@@ -2186,6 +2218,12 @@ async function routeEnvelopeToVpThread(sessionId, vpId, envelope) {
|
|
|
2186
2218
|
if (!thread) return null;
|
|
2187
2219
|
rememberThreadMessage(thread, envelope?.msg);
|
|
2188
2220
|
const turnId = `${randomUUID().slice(0, 8)}:${vpId}`;
|
|
2221
|
+
if (hasRepoApproval) {
|
|
2222
|
+
if (!bindRepoApprovalCapability(repoApproval, { sessionId, recipientVpId: vpId, turnId })) {
|
|
2223
|
+
return null;
|
|
2224
|
+
}
|
|
2225
|
+
turnRepoApprovals.set(turnId, { capability: repoApproval, sessionId, vpId });
|
|
2226
|
+
}
|
|
2189
2227
|
const perfTraceId = envelope?._perfTraceId || envelope?.perfTraceId || null;
|
|
2190
2228
|
if (perfTraceId) {
|
|
2191
2229
|
recordAgentPerfTrace(ctx.CONFIG, {
|
|
@@ -2357,6 +2395,7 @@ function ensureDriverRunning(sessionId, vpId, threadId = 'main') {
|
|
|
2357
2395
|
} catch (err) {
|
|
2358
2396
|
console.warn('[Yeaft] driveVp: runVpTurn failed', vpId, err?.message || err);
|
|
2359
2397
|
} finally {
|
|
2398
|
+
revokeTurnRepoApproval(turnId);
|
|
2360
2399
|
turnAbortCtrls.delete(turnId);
|
|
2361
2400
|
turnAbortMeta.delete(turnId);
|
|
2362
2401
|
if (vpAborts.get(key) === vpAbort) vpAborts.delete(key);
|
|
@@ -2491,6 +2530,7 @@ export async function __testResetVpState() {
|
|
|
2491
2530
|
if (Array.isArray(inbox)) inbox.length = 0;
|
|
2492
2531
|
}
|
|
2493
2532
|
vpThreads.clear();
|
|
2533
|
+
for (const turnId of turnRepoApprovals.keys()) revokeTurnRepoApproval(turnId);
|
|
2494
2534
|
turnAbortCtrls.clear();
|
|
2495
2535
|
turnAbortMeta.clear();
|
|
2496
2536
|
await __testDrainVpDrivers();
|
|
@@ -3921,7 +3961,6 @@ function buildVpPersona(vpId) {
|
|
|
3921
3961
|
}
|
|
3922
3962
|
}
|
|
3923
3963
|
|
|
3924
|
-
|
|
3925
3964
|
/**
|
|
3926
3965
|
* Install the dream pipeline progress sink and runtime settings bridge.
|
|
3927
3966
|
* Thread scheduling is owned by the group VP runtime below, not by mutable
|
|
@@ -5046,7 +5085,7 @@ function resolveCollabToolPolicy(sessionMeta) {
|
|
|
5046
5085
|
return vpCount > 1 ? COLLAB_TOOL_POLICY.MULTI_VP : COLLAB_TOOL_POLICY.SINGLE_VP;
|
|
5047
5086
|
}
|
|
5048
5087
|
|
|
5049
|
-
export function buildVpQueryOpts({ vpId, sessionCoordinator, sessionId, envelope, threadId = 'main' }) {
|
|
5088
|
+
export function buildVpQueryOpts({ vpId, sessionCoordinator, sessionId, envelope, threadId = 'main', turnId = null }) {
|
|
5050
5089
|
// Read the session meta once and reuse for defaultVpId fallback,
|
|
5051
5090
|
// announcement injection, and roster prompt context. Calling getMeta()
|
|
5052
5091
|
// twice per turn is wasteful — and (more importantly) opens a window
|
|
@@ -5083,6 +5122,7 @@ export function buildVpQueryOpts({ vpId, sessionCoordinator, sessionId, envelope
|
|
|
5083
5122
|
if (!resolvedVpId) return undefined;
|
|
5084
5123
|
|
|
5085
5124
|
const out = { senderVpId: resolvedVpId, threadId: threadId || 'main' };
|
|
5125
|
+
if (typeof turnId === 'string' && turnId.trim()) out.vpTurnId = turnId.trim();
|
|
5086
5126
|
if (typeof sessionId === 'string' && sessionId.trim()) {
|
|
5087
5127
|
out.sessionId = sessionId.trim();
|
|
5088
5128
|
}
|
|
@@ -5110,7 +5150,9 @@ export function buildVpQueryOpts({ vpId, sessionCoordinator, sessionId, envelope
|
|
|
5110
5150
|
if (persona) out.vpPersona = persona;
|
|
5111
5151
|
if (sessionCoordinator && typeof sessionCoordinator.ingest === 'function') {
|
|
5112
5152
|
try {
|
|
5113
|
-
out.router = createRouter({
|
|
5153
|
+
out.router = createRouter({
|
|
5154
|
+
coordinator: sessionCoordinator,
|
|
5155
|
+
});
|
|
5114
5156
|
} catch {
|
|
5115
5157
|
// Router build failure is non-fatal.
|
|
5116
5158
|
}
|
|
@@ -5304,6 +5346,7 @@ async function runVpTurnWithEscalation(args) {
|
|
|
5304
5346
|
graceMs: ESCALATE_AFTER_ABORT_MS,
|
|
5305
5347
|
onEscalate: () => {
|
|
5306
5348
|
if (escalationState.escalated) return;
|
|
5349
|
+
revokeTurnRepoApproval(turnId);
|
|
5307
5350
|
escalationState.escalated = true;
|
|
5308
5351
|
escalationState.terminalEmitted = true;
|
|
5309
5352
|
console.error(
|
|
@@ -5522,6 +5565,7 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
|
|
|
5522
5565
|
queryTimer = setTimeout(() => {
|
|
5523
5566
|
if (!vpAbort.signal.aborted) {
|
|
5524
5567
|
console.error(`[Yeaft] query timeout after ${queryTimeoutMs / 1000}s of silence — aborting VP ${vpId}`);
|
|
5568
|
+
revokeTurnRepoApproval(turnId);
|
|
5525
5569
|
try { vpAbort.abort(); } catch { /* best-effort */ }
|
|
5526
5570
|
}
|
|
5527
5571
|
}, queryTimeoutMs);
|
|
@@ -5558,6 +5602,7 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
|
|
|
5558
5602
|
sessionId,
|
|
5559
5603
|
envelope: inboundEnvelope,
|
|
5560
5604
|
threadId,
|
|
5605
|
+
turnId,
|
|
5561
5606
|
});
|
|
5562
5607
|
if (queryOpts) {
|
|
5563
5608
|
const envelopeProjectContext = normalizeProjectContext(inboundEnvelope?._projectContext, sessionId);
|
|
@@ -6219,6 +6264,10 @@ function emitQueuedTurnAbort(meta, turnId) {
|
|
|
6219
6264
|
}
|
|
6220
6265
|
|
|
6221
6266
|
function abortAllVpRuntime(aborted, sessionId = null) {
|
|
6267
|
+
for (const [turnId, meta] of Array.from(turnAbortMeta.entries())) {
|
|
6268
|
+
if (sessionId && meta?.sessionId !== sessionId) continue;
|
|
6269
|
+
revokeTurnRepoApproval(turnId);
|
|
6270
|
+
}
|
|
6222
6271
|
for (const [key, ctrl] of vpAborts) {
|
|
6223
6272
|
if (sessionId && !key.startsWith(`${sessionId}::`)) continue;
|
|
6224
6273
|
try {
|
|
@@ -6261,6 +6310,7 @@ export function handleYeaftAbortThread(_msg = {}) {
|
|
|
6261
6310
|
const meta = turnAbortMeta.get(turnId);
|
|
6262
6311
|
if ((meta?.threadId || 'main') !== targetThreadId) continue;
|
|
6263
6312
|
try { if (!ctrl.signal.aborted) { ctrl.abort(); aborted.push(turnId); } } catch { /* best-effort */ }
|
|
6313
|
+
revokeTurnRepoApproval(turnId);
|
|
6264
6314
|
turnAbortCtrls.delete(turnId);
|
|
6265
6315
|
turnAbortMeta.delete(turnId);
|
|
6266
6316
|
}
|
|
@@ -6279,6 +6329,7 @@ export function handleYeaftAbortThread(_msg = {}) {
|
|
|
6279
6329
|
for (const [turnId, ctrl] of turnAbortCtrls) {
|
|
6280
6330
|
try { if (!ctrl.signal.aborted) { ctrl.abort(); aborted.push(turnId); } } catch { /* best-effort */ }
|
|
6281
6331
|
}
|
|
6332
|
+
for (const turnId of turnAbortMeta.keys()) revokeTurnRepoApproval(turnId);
|
|
6282
6333
|
turnAbortCtrls.clear();
|
|
6283
6334
|
turnAbortMeta.clear();
|
|
6284
6335
|
abortAllVpRuntime(aborted);
|
|
@@ -6305,10 +6356,12 @@ export function handleYeaftAbortAll(msg = {}) {
|
|
|
6305
6356
|
const meta = turnAbortMeta.get(turnId);
|
|
6306
6357
|
if (sessionId && meta?.sessionId !== sessionId) continue;
|
|
6307
6358
|
try { if (!ctrl.signal.aborted) { ctrl.abort(); aborted.push(turnId); } } catch { /* best-effort */ }
|
|
6359
|
+
revokeTurnRepoApproval(turnId);
|
|
6308
6360
|
turnAbortCtrls.delete(turnId);
|
|
6309
6361
|
turnAbortMeta.delete(turnId);
|
|
6310
6362
|
}
|
|
6311
6363
|
if (!sessionId) {
|
|
6364
|
+
for (const turnId of turnAbortMeta.keys()) revokeTurnRepoApproval(turnId);
|
|
6312
6365
|
turnAbortCtrls.clear();
|
|
6313
6366
|
turnAbortMeta.clear();
|
|
6314
6367
|
}
|
|
@@ -6355,6 +6408,7 @@ export function handleYeaftAbortTurn(msg = {}) {
|
|
|
6355
6408
|
success = true;
|
|
6356
6409
|
abortedTurnIds.push(turnId);
|
|
6357
6410
|
}
|
|
6411
|
+
revokeTurnRepoApproval(turnId);
|
|
6358
6412
|
turnAbortCtrls.delete(turnId);
|
|
6359
6413
|
turnAbortMeta.delete(turnId);
|
|
6360
6414
|
}
|
|
@@ -7665,6 +7719,7 @@ export async function resetYeaftSession() {
|
|
|
7665
7719
|
try { if (!ctrl.signal.aborted) ctrl.abort(); } catch { /* best-effort */ }
|
|
7666
7720
|
}
|
|
7667
7721
|
vpAborts.clear();
|
|
7722
|
+
for (const turnId of turnAbortMeta.keys()) revokeTurnRepoApproval(turnId);
|
|
7668
7723
|
turnAbortCtrls.clear();
|
|
7669
7724
|
turnAbortMeta.clear();
|
|
7670
7725
|
vpInboxes.clear();
|
|
@@ -8143,6 +8198,7 @@ export const __testHooks = {
|
|
|
8143
8198
|
return loadAndBroadcastYeaftSkillSlashCommands();
|
|
8144
8199
|
},
|
|
8145
8200
|
resetAbortState() {
|
|
8201
|
+
for (const turnId of turnRepoApprovals.keys()) revokeTurnRepoApproval(turnId);
|
|
8146
8202
|
turnAbortCtrls.clear();
|
|
8147
8203
|
turnAbortMeta.clear();
|
|
8148
8204
|
vpAborts.clear();
|
|
@@ -8208,7 +8264,9 @@ export const __testHooks = {
|
|
|
8208
8264
|
});
|
|
8209
8265
|
const entry = makeGroupContextStub();
|
|
8210
8266
|
entry.coord = coord;
|
|
8211
|
-
entry.router = createRouter({
|
|
8267
|
+
entry.router = createRouter({
|
|
8268
|
+
coordinator: coord,
|
|
8269
|
+
});
|
|
8212
8270
|
entry.sessionHandle = group;
|
|
8213
8271
|
entry.historyHydrated = true;
|
|
8214
8272
|
sessionContexts.set(sessionId, entry);
|