pi-advisor-flow 0.8.2-dev.1 → 0.8.3-dev.1
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/CHANGELOG.md +13 -0
- package/dist/index.js +319 -184
- package/extensions/index.ts +12 -3
- package/package.json +4 -4
- package/src/commands/lifecycle.ts +1 -2
- package/src/commands/manual-consultation.ts +3 -3
- package/src/commands/runtime.ts +3 -1
- package/src/commands/types.ts +3 -0
- package/src/herdr-block.ts +15 -3
- package/src/herdr.ts +58 -8
- package/src/jev/client.ts +14 -6
- package/src/redaction.ts +6 -1
- package/src/session-state.ts +30 -4
- package/src/tools/gate-policy.ts +8 -5
- package/src/tools/jev-filter.ts +3 -2
- package/src/tools/jev-turn-gate.ts +45 -29
- package/src/tools/loop-gate.ts +32 -12
- package/src/tools/register-ask-advisor.ts +181 -144
- package/src/tools/register-lifecycle.ts +16 -4
- package/src/tools/registration.ts +4 -0
- package/src/tools/types.ts +5 -0
- package/src/ui/jev-setup-submenu.ts +8 -2
package/dist/index.js
CHANGED
|
@@ -1454,10 +1454,12 @@ import net from "node:net";
|
|
|
1454
1454
|
var REDACTION_MARKER = "[REDACTED SECRET]";
|
|
1455
1455
|
var PEM_BEGIN_PATTERN = /-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----/giu;
|
|
1456
1456
|
var PEM_END_PATTERN = /-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/iu;
|
|
1457
|
+
var CREDENTIAL_NAME = String.raw`(?:api[_-]?key|token|secret|password|passwd)`;
|
|
1458
|
+
var SECRET_ASSIGNMENT_PATTERN = new RegExp(String.raw`(?:(?:"(?:[a-z0-9]+[_-])*?${CREDENTIAL_NAME}"|'(?:[a-z0-9]+[_-])*?${CREDENTIAL_NAME}')|(?:\b|_)${CREDENTIAL_NAME})\s*[:=]\s*(?:"(?:\\.|[^"])*(?:"|$)|'(?:\\.|[^'])*(?:'|$)|[^\s"'&,;)}\]]+)`, "giu");
|
|
1457
1459
|
var SECRET_PATTERNS = [
|
|
1458
1460
|
/-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/giu,
|
|
1459
1461
|
/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/giu,
|
|
1460
|
-
|
|
1462
|
+
SECRET_ASSIGNMENT_PATTERN,
|
|
1461
1463
|
/(?<scheme>[a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/giu,
|
|
1462
1464
|
/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/gu,
|
|
1463
1465
|
/\b(?:aws_secret_access_key|aws_session_token)\s*[:=]\s*[^\s"'&,;)}\]]+/giu
|
|
@@ -1495,9 +1497,6 @@ var nextSequence = () => {
|
|
|
1495
1497
|
return sequence;
|
|
1496
1498
|
};
|
|
1497
1499
|
var emitBlocked;
|
|
1498
|
-
var setHerdrBlockedEmitter = (emitter) => {
|
|
1499
|
-
emitBlocked = emitter;
|
|
1500
|
-
};
|
|
1501
1500
|
var safeEmitBlocked = (active, label = "Advisor blocked") => {
|
|
1502
1501
|
try {
|
|
1503
1502
|
emitBlocked?.(active, label);
|
|
@@ -1538,9 +1537,11 @@ var createHerdrNotificationRequest = (title, body) => ({
|
|
|
1538
1537
|
// src/herdr-block.ts
|
|
1539
1538
|
class HerdrAdvisorBlock {
|
|
1540
1539
|
#blocked = false;
|
|
1540
|
+
emitBlocked;
|
|
1541
1541
|
report;
|
|
1542
1542
|
enabled;
|
|
1543
|
-
constructor(report = sendToHerdr, enabled = () => true) {
|
|
1543
|
+
constructor(report = sendToHerdr, enabled = () => true, emitBlocked = safeEmitBlocked) {
|
|
1544
|
+
this.emitBlocked = emitBlocked;
|
|
1544
1545
|
this.report = report;
|
|
1545
1546
|
this.enabled = enabled;
|
|
1546
1547
|
}
|
|
@@ -1551,7 +1552,7 @@ class HerdrAdvisorBlock {
|
|
|
1551
1552
|
const label = cleanNotification(reason, 200);
|
|
1552
1553
|
const wasBlocked = this.#blocked;
|
|
1553
1554
|
if (!wasBlocked) {
|
|
1554
|
-
safeEmitBlocked(true, label);
|
|
1555
|
+
this.safeEmitBlocked(true, label);
|
|
1555
1556
|
}
|
|
1556
1557
|
this.#blocked = true;
|
|
1557
1558
|
this.safeReport({ blocked: label });
|
|
@@ -1562,7 +1563,7 @@ class HerdrAdvisorBlock {
|
|
|
1562
1563
|
if (!wasBlocked) {
|
|
1563
1564
|
return;
|
|
1564
1565
|
}
|
|
1565
|
-
safeEmitBlocked(false);
|
|
1566
|
+
this.safeEmitBlocked(false, "Advisor blocked");
|
|
1566
1567
|
try {
|
|
1567
1568
|
this.report({
|
|
1568
1569
|
id: `${BLOCK_SOURCE}:${nextSequence()}`,
|
|
@@ -1578,6 +1579,14 @@ class HerdrAdvisorBlock {
|
|
|
1578
1579
|
});
|
|
1579
1580
|
} catch {}
|
|
1580
1581
|
}
|
|
1582
|
+
safeEmitBlocked(active, label) {
|
|
1583
|
+
try {
|
|
1584
|
+
this.emitBlocked(active, label);
|
|
1585
|
+
return true;
|
|
1586
|
+
} catch {
|
|
1587
|
+
return false;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1581
1590
|
safeReport(labels) {
|
|
1582
1591
|
try {
|
|
1583
1592
|
this.report({
|
|
@@ -1612,6 +1621,8 @@ var metadataRequest = (clear) => ({
|
|
|
1612
1621
|
|
|
1613
1622
|
class HerdrAdvisorActivity {
|
|
1614
1623
|
#activeConsultations = 0;
|
|
1624
|
+
#activeByOwner = new Map;
|
|
1625
|
+
#defaultOwner = Symbol("default Herdr Advisor activity");
|
|
1615
1626
|
report;
|
|
1616
1627
|
enabled;
|
|
1617
1628
|
constructor(report = sendToHerdr, enabled = () => true) {
|
|
@@ -1619,29 +1630,67 @@ class HerdrAdvisorActivity {
|
|
|
1619
1630
|
this.enabled = enabled;
|
|
1620
1631
|
}
|
|
1621
1632
|
start() {
|
|
1633
|
+
return this.startFor(this.#defaultOwner);
|
|
1634
|
+
}
|
|
1635
|
+
finish() {
|
|
1636
|
+
this.finishFor(this.#defaultOwner);
|
|
1637
|
+
}
|
|
1638
|
+
clear() {
|
|
1639
|
+
this.clearFor(this.#defaultOwner);
|
|
1640
|
+
}
|
|
1641
|
+
createScope() {
|
|
1642
|
+
const owner = Symbol("Herdr Advisor runtime");
|
|
1643
|
+
return {
|
|
1644
|
+
clear: () => this.clearFor(owner),
|
|
1645
|
+
start: () => this.startFor(owner)
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
startFor(owner) {
|
|
1649
|
+
const lease = Symbol("Herdr activity lease");
|
|
1622
1650
|
if (!this.enabled()) {
|
|
1623
|
-
return;
|
|
1651
|
+
return () => this.finishLease(owner, lease);
|
|
1652
|
+
}
|
|
1653
|
+
let leases = this.#activeByOwner.get(owner);
|
|
1654
|
+
if (!leases) {
|
|
1655
|
+
leases = new Set;
|
|
1656
|
+
this.#activeByOwner.set(owner, leases);
|
|
1624
1657
|
}
|
|
1658
|
+
leases.add(lease);
|
|
1625
1659
|
this.#activeConsultations += 1;
|
|
1626
1660
|
if (this.#activeConsultations === 1) {
|
|
1627
1661
|
this.safeReport(false);
|
|
1628
1662
|
}
|
|
1663
|
+
return () => this.finishLease(owner, lease);
|
|
1629
1664
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1665
|
+
finishFor(owner) {
|
|
1666
|
+
const lease = this.#activeByOwner.get(owner)?.values().next().value;
|
|
1667
|
+
if (lease) {
|
|
1668
|
+
this.finishLease(owner, lease);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
finishLease(owner, lease) {
|
|
1672
|
+
const leases = this.#activeByOwner.get(owner);
|
|
1673
|
+
if (!leases?.delete(lease)) {
|
|
1632
1674
|
return;
|
|
1633
1675
|
}
|
|
1676
|
+
if (leases.size === 0) {
|
|
1677
|
+
this.#activeByOwner.delete(owner);
|
|
1678
|
+
}
|
|
1634
1679
|
this.#activeConsultations -= 1;
|
|
1635
1680
|
if (this.#activeConsultations === 0) {
|
|
1636
1681
|
this.safeReport(true);
|
|
1637
1682
|
}
|
|
1638
1683
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1684
|
+
clearFor(owner) {
|
|
1685
|
+
const leases = this.#activeByOwner.get(owner);
|
|
1686
|
+
if (!leases?.size) {
|
|
1641
1687
|
return;
|
|
1642
1688
|
}
|
|
1643
|
-
this.#
|
|
1644
|
-
this.
|
|
1689
|
+
this.#activeByOwner.delete(owner);
|
|
1690
|
+
this.#activeConsultations -= leases.size;
|
|
1691
|
+
if (this.#activeConsultations === 0) {
|
|
1692
|
+
this.safeReport(true);
|
|
1693
|
+
}
|
|
1645
1694
|
}
|
|
1646
1695
|
safeReport(clear) {
|
|
1647
1696
|
try {
|
|
@@ -3859,6 +3908,7 @@ class AdvisorSessionState {
|
|
|
3859
3908
|
#ledger = freshAdviceLedger();
|
|
3860
3909
|
#usage = freshUsage();
|
|
3861
3910
|
#consumedCalls = 0;
|
|
3911
|
+
#callReservations = new Set;
|
|
3862
3912
|
#jev = new AdvisorJevLedgerState;
|
|
3863
3913
|
#sessionTurnOrdinal = 0;
|
|
3864
3914
|
#turnsSinceConsultation = 0;
|
|
@@ -3868,6 +3918,7 @@ class AdvisorSessionState {
|
|
|
3868
3918
|
this.#ledger = freshAdviceLedger();
|
|
3869
3919
|
this.#usage = freshUsage();
|
|
3870
3920
|
this.#consumedCalls = 0;
|
|
3921
|
+
this.#callReservations.clear();
|
|
3871
3922
|
this.#jev.reset();
|
|
3872
3923
|
this.#sessionTurnOrdinal = 0;
|
|
3873
3924
|
this.#turnsSinceConsultation = 0;
|
|
@@ -3901,14 +3952,34 @@ class AdvisorSessionState {
|
|
|
3901
3952
|
this.#repetition.interventions += 1;
|
|
3902
3953
|
return true;
|
|
3903
3954
|
}
|
|
3904
|
-
canConsult(limit) {
|
|
3905
|
-
|
|
3955
|
+
canConsult(limit, reservationId) {
|
|
3956
|
+
const reservations = this.#callReservations.size - Number(reservationId !== undefined && this.#callReservations.has(reservationId));
|
|
3957
|
+
return limit === undefined || this.#consumedCalls + reservations < limit;
|
|
3958
|
+
}
|
|
3959
|
+
reserveCall(id, limit) {
|
|
3960
|
+
if (this.#callReservations.has(id)) {
|
|
3961
|
+
return true;
|
|
3962
|
+
}
|
|
3963
|
+
if (!this.canConsult(limit)) {
|
|
3964
|
+
return false;
|
|
3965
|
+
}
|
|
3966
|
+
this.#callReservations.add(id);
|
|
3967
|
+
return true;
|
|
3968
|
+
}
|
|
3969
|
+
releaseCall(id) {
|
|
3970
|
+
this.#callReservations.delete(id);
|
|
3906
3971
|
}
|
|
3907
|
-
|
|
3972
|
+
clearCallReservations() {
|
|
3973
|
+
this.#callReservations.clear();
|
|
3974
|
+
}
|
|
3975
|
+
consumeCall(reservationId) {
|
|
3976
|
+
if (reservationId !== undefined) {
|
|
3977
|
+
this.#callReservations.delete(reservationId);
|
|
3978
|
+
}
|
|
3908
3979
|
this.#consumedCalls += 1;
|
|
3909
3980
|
}
|
|
3910
3981
|
remainingCalls(limit) {
|
|
3911
|
-
return limit === undefined ? undefined : Math.max(0, limit - this.#consumedCalls);
|
|
3982
|
+
return limit === undefined ? undefined : Math.max(0, limit - this.#consumedCalls - this.#callReservations.size);
|
|
3912
3983
|
}
|
|
3913
3984
|
get consumedCalls() {
|
|
3914
3985
|
return this.#consumedCalls;
|
|
@@ -4104,6 +4175,7 @@ var requestManualRender = (ctx) => {
|
|
|
4104
4175
|
|
|
4105
4176
|
class CommandRuntime {
|
|
4106
4177
|
advisorSessionState;
|
|
4178
|
+
herdrActivity;
|
|
4107
4179
|
manualConsultations = new Map;
|
|
4108
4180
|
manualProgress = new Map;
|
|
4109
4181
|
manualProgressTimers = new Map;
|
|
@@ -4118,6 +4190,7 @@ class CommandRuntime {
|
|
|
4118
4190
|
constructor(pi, dependencies = {}) {
|
|
4119
4191
|
this.pi = pi;
|
|
4120
4192
|
this.advisorSessionState = dependencies.sessionState ?? advisorSessionState;
|
|
4193
|
+
this.herdrActivity = dependencies.herdrActivity ?? herdrAdvisorActivity;
|
|
4121
4194
|
this.scoutStatus = dependencies.statusManager ?? new ScoutStatusManager(false);
|
|
4122
4195
|
this.requestAdvisor = dependencies.consult ?? ((ctx, question, signal, onChunk, onScout, gitContext) => consultAdvisor(ctx, question, signal, onChunk, "manual", gitContext, undefined, undefined, undefined, onScout));
|
|
4123
4196
|
}
|
|
@@ -4325,7 +4398,7 @@ var registerCommandLifecycle = (runtime, activateAdvisor) => {
|
|
|
4325
4398
|
runtime.manualConsultations.clear();
|
|
4326
4399
|
runtime.manualProgressTimers.clear();
|
|
4327
4400
|
runtime.manualProgress.clear();
|
|
4328
|
-
|
|
4401
|
+
runtime.herdrActivity.clear();
|
|
4329
4402
|
});
|
|
4330
4403
|
};
|
|
4331
4404
|
|
|
@@ -4676,14 +4749,19 @@ var connectionFailure = (error) => {
|
|
|
4676
4749
|
failure: new JevFailureError("network", `Jev connection failed: ${message}`)
|
|
4677
4750
|
};
|
|
4678
4751
|
};
|
|
4679
|
-
var sleepWithAbort = (signal, ms) =>
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
resolve
|
|
4685
|
-
|
|
4686
|
-
|
|
4752
|
+
var sleepWithAbort = (signal, ms) => {
|
|
4753
|
+
if (signal.aborted) {
|
|
4754
|
+
return Promise.resolve();
|
|
4755
|
+
}
|
|
4756
|
+
return new Promise((resolve) => {
|
|
4757
|
+
const timer = setTimeout(resolve, ms);
|
|
4758
|
+
timer.unref?.();
|
|
4759
|
+
signal.addEventListener("abort", () => {
|
|
4760
|
+
clearTimeout(timer);
|
|
4761
|
+
resolve();
|
|
4762
|
+
}, { once: true });
|
|
4763
|
+
});
|
|
4764
|
+
};
|
|
4687
4765
|
|
|
4688
4766
|
class JevClient {
|
|
4689
4767
|
#apiKey;
|
|
@@ -4729,7 +4807,7 @@ class JevClient {
|
|
|
4729
4807
|
let outcome = {};
|
|
4730
4808
|
for (let attempt = 1;; attempt += 1) {
|
|
4731
4809
|
outcome = await this.#attempt(body, deadline.signal);
|
|
4732
|
-
if (!outcome.retryable || attempt >= MAX_ATTEMPTS) {
|
|
4810
|
+
if (!outcome.retryable || attempt >= MAX_ATTEMPTS || deadline.signal.aborted) {
|
|
4733
4811
|
break;
|
|
4734
4812
|
}
|
|
4735
4813
|
await sleepWithAbort(deadline.signal, RETRY_BACKOFF_MS);
|
|
@@ -4759,12 +4837,12 @@ class JevClient {
|
|
|
4759
4837
|
if (deadlineHit && outcome.failure?.category !== "auth") {
|
|
4760
4838
|
throw new JevFailureError("timeout", `Jev call exceeded its ${this.#timeoutMs} ms wall-time budget.`);
|
|
4761
4839
|
}
|
|
4762
|
-
if (outcome.failure) {
|
|
4763
|
-
throw outcome.failure;
|
|
4764
|
-
}
|
|
4765
4840
|
if (signal?.aborted) {
|
|
4766
4841
|
throw new Error("Jev call aborted by the caller.");
|
|
4767
4842
|
}
|
|
4843
|
+
if (outcome.failure) {
|
|
4844
|
+
throw outcome.failure;
|
|
4845
|
+
}
|
|
4768
4846
|
throw new JevFailureError("error", "Jev call failed.");
|
|
4769
4847
|
}
|
|
4770
4848
|
async#attempt(body, signal) {
|
|
@@ -5227,11 +5305,12 @@ var screenWithJev = async (ctx, session, options, deps, normalizedQuestion) => {
|
|
|
5227
5305
|
session.recordJevFilterAllowed();
|
|
5228
5306
|
return allow();
|
|
5229
5307
|
} catch (error) {
|
|
5308
|
+
if (options.signal?.aborted) {
|
|
5309
|
+
throw error;
|
|
5310
|
+
}
|
|
5230
5311
|
session.recordJevFilterFailure();
|
|
5231
5312
|
if (error instanceof JevFailureError) {
|
|
5232
5313
|
notifyOutageOnce(ctx, error.category, error.message);
|
|
5233
|
-
} else if (options.signal?.aborted) {
|
|
5234
|
-
throw error;
|
|
5235
5314
|
} else {
|
|
5236
5315
|
notifyOutageOnce(ctx, "error", error instanceof Error ? error.message : String(error));
|
|
5237
5316
|
}
|
|
@@ -5266,7 +5345,6 @@ var screeningSkipText = (outcome) => outcome.kind === "repeat" && outcome.reatta
|
|
|
5266
5345
|
|
|
5267
5346
|
// src/commands/manual-consultation.ts
|
|
5268
5347
|
var startManualConsultation = async (runtime, ctx, question, controller, scoutStatusToken, progress, gitContext) => {
|
|
5269
|
-
herdrAdvisorActivity.start();
|
|
5270
5348
|
progress.phase = "preparing";
|
|
5271
5349
|
runtime.requestManualRender(ctx);
|
|
5272
5350
|
if (ctx.hasUI) {
|
|
@@ -5279,6 +5357,7 @@ var startManualConsultation = async (runtime, ctx, question, controller, scoutSt
|
|
|
5279
5357
|
}, 80);
|
|
5280
5358
|
runtime.manualProgressTimers.set(controller, timer);
|
|
5281
5359
|
}
|
|
5360
|
+
const finishHerdrActivity = runtime.herdrActivity.start();
|
|
5282
5361
|
let scoutDetails;
|
|
5283
5362
|
try {
|
|
5284
5363
|
const { adviceId, markdown, usage } = await runtime.requestAdvisor(ctx, question, controller.signal, (thinking, text) => {
|
|
@@ -5362,6 +5441,7 @@ ${markdown}`,
|
|
|
5362
5441
|
notify(ctx, `Advisor consultation failed: ${message}`, "error");
|
|
5363
5442
|
notifyHerdrAdvisorFailure("Advisor consultation failed", message);
|
|
5364
5443
|
} finally {
|
|
5444
|
+
finishHerdrActivity();
|
|
5365
5445
|
if (controller.signal.aborted) {
|
|
5366
5446
|
progress.phase = "cancelled";
|
|
5367
5447
|
}
|
|
@@ -5373,7 +5453,6 @@ ${markdown}`,
|
|
|
5373
5453
|
runtime.requestManualRender(ctx);
|
|
5374
5454
|
runtime.scoutStatus.release(ctx, scoutStatusToken);
|
|
5375
5455
|
runtime.manualConsultations.delete(controller);
|
|
5376
|
-
herdrAdvisorActivity.finish();
|
|
5377
5456
|
}
|
|
5378
5457
|
};
|
|
5379
5458
|
|
|
@@ -5886,7 +5965,7 @@ class JevSetupSubmenu {
|
|
|
5886
5965
|
}
|
|
5887
5966
|
actions() {
|
|
5888
5967
|
if (!this.credentials) {
|
|
5889
|
-
return ["enter-key", "done"];
|
|
5968
|
+
return this.options.currentValue === "On" ? ["enter-key", "disable", "done"] : ["enter-key", "done"];
|
|
5890
5969
|
}
|
|
5891
5970
|
const enabled = this.options.currentValue === "On";
|
|
5892
5971
|
const actions = [enabled ? "verify-again" : "verify-enable"];
|
|
@@ -5942,8 +6021,12 @@ class JevSetupSubmenu {
|
|
|
5942
6021
|
return;
|
|
5943
6022
|
}
|
|
5944
6023
|
const result = await clear();
|
|
5945
|
-
this.credentials = undefined;
|
|
5946
6024
|
this.notice = result.message;
|
|
6025
|
+
if (!result.ok) {
|
|
6026
|
+
this.options.tui.requestRender();
|
|
6027
|
+
return;
|
|
6028
|
+
}
|
|
6029
|
+
this.credentials = undefined;
|
|
5947
6030
|
this.options.done("Off");
|
|
5948
6031
|
}
|
|
5949
6032
|
async verifyAndEnable() {
|
|
@@ -6970,7 +7053,7 @@ var gateDecisionEffect = (decision, failureMode) => {
|
|
|
6970
7053
|
}
|
|
6971
7054
|
return decision === "blocked" ? gateFailureEffectForMode(failureMode) : "tool-blocked";
|
|
6972
7055
|
};
|
|
6973
|
-
var failureEffect = (category, message, ctx, session, failureMode) => {
|
|
7056
|
+
var failureEffect = (category, message, ctx, session, failureMode, herdrBlock = herdrAdvisorBlock) => {
|
|
6974
7057
|
const reason = `Advisor gate ${category}: ${message}`;
|
|
6975
7058
|
notifyLocalFailure(ctx, message, failureMode === "block-session");
|
|
6976
7059
|
notifyHerdrAdvisorFailure("Advisor gate failure", reason);
|
|
@@ -6981,13 +7064,13 @@ var failureEffect = (category, message, ctx, session, failureMode) => {
|
|
|
6981
7064
|
return { block: true, effect: "tool-blocked", reason };
|
|
6982
7065
|
}
|
|
6983
7066
|
session.block(reason);
|
|
6984
|
-
|
|
7067
|
+
herdrBlock.set(reason);
|
|
6985
7068
|
if (advisorBlockOnBlockedRef) {
|
|
6986
7069
|
ctx.abort();
|
|
6987
7070
|
}
|
|
6988
7071
|
return { block: true, effect: "session-blocked", reason };
|
|
6989
7072
|
};
|
|
6990
|
-
var blockedDecisionEffect = (reason, ctx, session, failureMode) => {
|
|
7073
|
+
var blockedDecisionEffect = (reason, ctx, session, failureMode, herdrBlock = herdrAdvisorBlock) => {
|
|
6991
7074
|
if (failureMode === "warn-and-continue") {
|
|
6992
7075
|
if (ctx.hasUI) {
|
|
6993
7076
|
ctx.ui.notify("Advisor gate returned blocked; continuing by configuration.", "warning");
|
|
@@ -6998,7 +7081,7 @@ var blockedDecisionEffect = (reason, ctx, session, failureMode) => {
|
|
|
6998
7081
|
return { block: true, effect: "tool-blocked", reason };
|
|
6999
7082
|
}
|
|
7000
7083
|
session.block(reason);
|
|
7001
|
-
|
|
7084
|
+
herdrBlock.set(reason);
|
|
7002
7085
|
if (advisorBlockOnBlockedRef) {
|
|
7003
7086
|
ctx.abort();
|
|
7004
7087
|
}
|
|
@@ -7008,7 +7091,7 @@ var reserveAdvisorCall = (event, ctx, session, reservedCalls) => {
|
|
|
7008
7091
|
if (event.toolName !== "ask_advisor" || isSimpleMode()) {
|
|
7009
7092
|
return;
|
|
7010
7093
|
}
|
|
7011
|
-
if (!session.
|
|
7094
|
+
if (!session.reserveCall(event.toolCallId, getAdvisorMaxCallsPerSession())) {
|
|
7012
7095
|
const message = "Advisor call budget exhausted for this session.";
|
|
7013
7096
|
if (ctx.hasUI) {
|
|
7014
7097
|
ctx.ui.notify(message, "warning");
|
|
@@ -7031,15 +7114,22 @@ var turnGateQuestion = {
|
|
|
7031
7114
|
};
|
|
7032
7115
|
var outageNotifier2 = createOutageNotifier((category, message) => `Advisor Jev turn gate failed (${category}); continuing without a proactive consultation. ${message}`);
|
|
7033
7116
|
var notifyFailureOnce = outageNotifier2.notify;
|
|
7117
|
+
var shouldRunTurnGate = (registration, ctx, session) => {
|
|
7118
|
+
const interval = advisorJevTurnGateEveryTurnsRef;
|
|
7119
|
+
return interval > 0 && session.turnsSinceConsultation > 0 && session.turnsSinceConsultation % interval === 0 && !isSimpleMode() && !session.blocked && registration.activeTools().includes("ask_advisor") && advisorModelIsAllowed(ctx) && session.canConsult(getAdvisorMaxCallsPerSession());
|
|
7120
|
+
};
|
|
7121
|
+
var consumeTurnGateBudget = (session, shouldConsult) => {
|
|
7122
|
+
if (!shouldConsult || !session.canConsult(getAdvisorMaxCallsPerSession())) {
|
|
7123
|
+
return false;
|
|
7124
|
+
}
|
|
7125
|
+
session.consumeCall();
|
|
7126
|
+
return true;
|
|
7127
|
+
};
|
|
7034
7128
|
var resetJevTurnGateNotification = outageNotifier2.reset;
|
|
7035
7129
|
var handleJevTurnEnd = async (registration, ctx) => {
|
|
7036
7130
|
const { session } = registration;
|
|
7037
7131
|
session.recordCompletedTurn();
|
|
7038
|
-
|
|
7039
|
-
if (interval <= 0 || session.turnsSinceConsultation <= 0 || session.turnsSinceConsultation % interval !== 0) {
|
|
7040
|
-
return;
|
|
7041
|
-
}
|
|
7042
|
-
if (isSimpleMode() || session.blocked || !registration.activeTools().includes("ask_advisor") || !advisorModelIsAllowed(ctx) || !session.canConsult(getAdvisorMaxCallsPerSession())) {
|
|
7132
|
+
if (!shouldRunTurnGate(registration, ctx, session)) {
|
|
7043
7133
|
return;
|
|
7044
7134
|
}
|
|
7045
7135
|
const consult = registration.deps?.consult ?? registration.consult;
|
|
@@ -7057,21 +7147,21 @@ var handleJevTurnEnd = async (registration, ctx) => {
|
|
|
7057
7147
|
});
|
|
7058
7148
|
session.recordJevGateCheck(result.usage);
|
|
7059
7149
|
const shouldConsult = composeTurnGateVerdict(result.answers, advisorJevTurnGateNoulThresholdRef);
|
|
7060
|
-
if (!shouldConsult) {
|
|
7150
|
+
if (!consumeTurnGateBudget(session, shouldConsult)) {
|
|
7061
7151
|
return;
|
|
7062
7152
|
}
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
registration.send({
|
|
7066
|
-
content: "Proactive Advisor turn review",
|
|
7067
|
-
customType: "advisor-turn-gate-call",
|
|
7068
|
-
details: {
|
|
7069
|
-
question: `Turn gate: ${session.turnsSinceConsultation} turns without a consultation`,
|
|
7070
|
-
turn: session.sessionTurnOrdinal
|
|
7071
|
-
},
|
|
7072
|
-
display: true
|
|
7073
|
-
});
|
|
7153
|
+
const herdrActivity = registration.herdrActivity ?? herdrAdvisorActivity;
|
|
7154
|
+
const finishHerdrActivity = herdrActivity.start();
|
|
7074
7155
|
try {
|
|
7156
|
+
registration.send({
|
|
7157
|
+
content: "Proactive Advisor turn review",
|
|
7158
|
+
customType: "advisor-turn-gate-call",
|
|
7159
|
+
details: {
|
|
7160
|
+
question: `Turn gate: ${session.turnsSinceConsultation} turns without a consultation`,
|
|
7161
|
+
turn: session.sessionTurnOrdinal
|
|
7162
|
+
},
|
|
7163
|
+
display: true
|
|
7164
|
+
});
|
|
7075
7165
|
const consulted = await consult(ctx, undefined, ctx.signal, undefined, "turn-gate");
|
|
7076
7166
|
session.recordJevGateConsultation();
|
|
7077
7167
|
session.resetTurnsSinceConsultation();
|
|
@@ -7095,7 +7185,7 @@ var handleJevTurnEnd = async (registration, ctx) => {
|
|
|
7095
7185
|
display: true
|
|
7096
7186
|
});
|
|
7097
7187
|
} finally {
|
|
7098
|
-
|
|
7188
|
+
finishHerdrActivity();
|
|
7099
7189
|
}
|
|
7100
7190
|
} catch (error) {
|
|
7101
7191
|
session.recordJevGateFailure();
|
|
@@ -7254,6 +7344,7 @@ var claimTrackedHandoff = (session, includeTrackedFiles) => {
|
|
|
7254
7344
|
};
|
|
7255
7345
|
var registerAskAdvisorTool = ({
|
|
7256
7346
|
consult: requestAdvisor,
|
|
7347
|
+
herdrActivity,
|
|
7257
7348
|
pi,
|
|
7258
7349
|
reservedCalls,
|
|
7259
7350
|
screen,
|
|
@@ -7262,128 +7353,159 @@ var registerAskAdvisorTool = ({
|
|
|
7262
7353
|
pi.registerTool({
|
|
7263
7354
|
description: "Consult the on-demand Advisor model for strategic guidance. Call with an empty object for a contextual review; attach an optional draft for concrete plan or completion review. If the Advisor explicitly names a missing file, you may make a sequential follow-up call with includeTrackedFiles when enabled and relevant.",
|
|
7264
7355
|
async execute(_id, params, signal, onUpdate, ctx) {
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7356
|
+
try {
|
|
7357
|
+
assertAdvisorModelAccess(ctx);
|
|
7358
|
+
} catch (error) {
|
|
7359
|
+
session.releaseCall(_id);
|
|
7360
|
+
reservedCalls.delete(_id);
|
|
7361
|
+
throw error;
|
|
7269
7362
|
}
|
|
7270
|
-
const
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
force: params.force,
|
|
7274
|
-
question: resolveAdvisorRequest(params.question),
|
|
7275
|
-
signal
|
|
7276
|
-
});
|
|
7277
|
-
if (screening.decision === "skip") {
|
|
7278
|
-
const skipText = screeningSkipText(screening);
|
|
7279
|
-
return {
|
|
7280
|
-
content: [{ text: skipText, type: "text" }],
|
|
7281
|
-
details: {
|
|
7282
|
-
jev: {
|
|
7283
|
-
kind: screening.kind,
|
|
7284
|
-
reason: screening.reason,
|
|
7285
|
-
skipped: true
|
|
7286
|
-
},
|
|
7287
|
-
text: skipText
|
|
7288
|
-
}
|
|
7289
|
-
};
|
|
7363
|
+
const simpleMode = isSimpleMode();
|
|
7364
|
+
if (!simpleMode && !session.reserveCall(_id, getAdvisorMaxCallsPerSession())) {
|
|
7365
|
+
throw new Error("Advisor call budget exhausted for this session.");
|
|
7290
7366
|
}
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7367
|
+
if (simpleMode) {
|
|
7368
|
+
session.releaseCall(_id);
|
|
7369
|
+
reservedCalls.delete(_id);
|
|
7370
|
+
} else {
|
|
7371
|
+
reservedCalls.add(_id);
|
|
7295
7372
|
}
|
|
7296
|
-
|
|
7297
|
-
let scoutDetails;
|
|
7298
|
-
const coalescedUpdate = createCoalescedUpdate((update) => onUpdate?.(update), ADVISOR_STREAM_UPDATE_INTERVAL_MS);
|
|
7299
|
-
const flushUpdate = () => {
|
|
7300
|
-
const result = coalescedUpdate.flush();
|
|
7301
|
-
if (result.failed) {
|
|
7302
|
-
throw result.error;
|
|
7303
|
-
}
|
|
7304
|
-
};
|
|
7373
|
+
let normalizedQuestion;
|
|
7305
7374
|
try {
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7375
|
+
if (!simpleMode && !session.canConsult(getAdvisorMaxCallsPerSession(), _id)) {
|
|
7376
|
+
throw new Error("Advisor call budget exhausted for this session.");
|
|
7377
|
+
}
|
|
7378
|
+
normalizedQuestion = normalizeScreeningQuestion(resolveAdvisorRequest(params.question));
|
|
7379
|
+
const screening = await screen(ctx, session, {
|
|
7380
|
+
draft: params.draft,
|
|
7381
|
+
force: params.force,
|
|
7382
|
+
question: resolveAdvisorRequest(params.question),
|
|
7383
|
+
signal
|
|
7384
|
+
});
|
|
7385
|
+
if (screening.decision === "skip") {
|
|
7386
|
+
const skipText = screeningSkipText(screening);
|
|
7387
|
+
session.releaseCall(_id);
|
|
7388
|
+
reservedCalls.delete(_id);
|
|
7389
|
+
return {
|
|
7390
|
+
content: [{ text: skipText, type: "text" }],
|
|
7391
|
+
details: {
|
|
7392
|
+
jev: {
|
|
7393
|
+
kind: screening.kind,
|
|
7394
|
+
reason: screening.reason,
|
|
7395
|
+
skipped: true
|
|
7396
|
+
},
|
|
7397
|
+
text: skipText
|
|
7398
|
+
}
|
|
7399
|
+
};
|
|
7400
|
+
}
|
|
7401
|
+
if (!simpleMode && !session.canConsult(getAdvisorMaxCallsPerSession(), _id)) {
|
|
7402
|
+
throw new Error("Advisor call budget exhausted for this session.");
|
|
7403
|
+
}
|
|
7404
|
+
claimTrackedHandoff(session, params.includeTrackedFiles);
|
|
7405
|
+
if (!simpleMode) {
|
|
7406
|
+
session.consumeCall(_id);
|
|
7407
|
+
reservedCalls.delete(_id);
|
|
7408
|
+
session.resetTurnsSinceConsultation();
|
|
7409
|
+
}
|
|
7410
|
+
} catch (error) {
|
|
7411
|
+
session.releaseCall(_id);
|
|
7412
|
+
reservedCalls.delete(_id);
|
|
7413
|
+
throw error;
|
|
7414
|
+
}
|
|
7415
|
+
const runConsultation = async () => {
|
|
7416
|
+
let scoutDetails;
|
|
7417
|
+
const coalescedUpdate = createCoalescedUpdate((update) => onUpdate?.(update), ADVISOR_STREAM_UPDATE_INTERVAL_MS);
|
|
7418
|
+
const flushUpdate = () => {
|
|
7419
|
+
const result = coalescedUpdate.flush();
|
|
7420
|
+
if (result.failed) {
|
|
7421
|
+
throw result.error;
|
|
7314
7422
|
}
|
|
7315
|
-
}
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7423
|
+
};
|
|
7424
|
+
const finishHerdrActivity = herdrActivity.start();
|
|
7425
|
+
try {
|
|
7426
|
+
const result = await requestAdvisor(ctx, resolveAdvisorRequest(params.question), signal, (t, tx) => coalescedUpdate.update({
|
|
7427
|
+
content: [{ text: tx, type: "text" }],
|
|
7319
7428
|
details: {
|
|
7320
7429
|
advisor: advisorRef,
|
|
7321
7430
|
question: resolveAdvisorRequest(params.question),
|
|
7322
|
-
scout: scoutDetails
|
|
7431
|
+
scout: scoutDetails,
|
|
7432
|
+
text: tx,
|
|
7433
|
+
thinking: t
|
|
7323
7434
|
}
|
|
7435
|
+
}), "executor-requested", params.gitContext === "none" ? "off" : params.gitContext, params.draft, params.includeUntracked, params.includeTrackedFiles, (event) => {
|
|
7436
|
+
scoutDetails = scoutDetailsFromEvent(event, scoutDetails);
|
|
7437
|
+
coalescedUpdate.update({
|
|
7438
|
+
content: [{ text: scoutDetails.text ?? "", type: "text" }],
|
|
7439
|
+
details: {
|
|
7440
|
+
advisor: advisorRef,
|
|
7441
|
+
question: resolveAdvisorRequest(params.question),
|
|
7442
|
+
scout: scoutDetails
|
|
7443
|
+
}
|
|
7444
|
+
});
|
|
7445
|
+
}, _id);
|
|
7446
|
+
flushUpdate();
|
|
7447
|
+
session.issueAdvice(result.adviceId, result.markdown, result.trigger, Boolean(result.draftBytes), normalizedQuestion);
|
|
7448
|
+
session.recordInvocation({
|
|
7449
|
+
cost: advisorUsageCost(result.usage),
|
|
7450
|
+
executionEffect: "continued",
|
|
7451
|
+
kind: "markdown",
|
|
7452
|
+
model: result.model,
|
|
7453
|
+
trigger: "executor-requested",
|
|
7454
|
+
usage: result.usage
|
|
7324
7455
|
});
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
thinking: result.thinkingText,
|
|
7348
|
-
trackedBytes: result.trackedBytes,
|
|
7349
|
-
untrackedBytes: result.untrackedBytes
|
|
7350
|
-
};
|
|
7351
|
-
if (usage) {
|
|
7352
|
-
details.usage = usage;
|
|
7353
|
-
}
|
|
7354
|
-
const response = {
|
|
7355
|
-
content: [
|
|
7356
|
-
{
|
|
7357
|
-
text: `Advisor (${result.model})
|
|
7456
|
+
const usage = snapshotAdvisorUsage(result.usage);
|
|
7457
|
+
const piUsage = advisorUsageForPi(result.usage);
|
|
7458
|
+
updateAdvisorUsageStatus(ctx, session);
|
|
7459
|
+
const details = {
|
|
7460
|
+
adviceId: result.adviceId,
|
|
7461
|
+
advisor: result.model,
|
|
7462
|
+
draftBytes: result.draftBytes,
|
|
7463
|
+
preferenceBytes: result.preferenceBytes,
|
|
7464
|
+
question: resolveAdvisorRequest(params.question),
|
|
7465
|
+
scout: scoutDetails,
|
|
7466
|
+
text: result.markdown,
|
|
7467
|
+
thinking: result.thinkingText,
|
|
7468
|
+
trackedBytes: result.trackedBytes,
|
|
7469
|
+
untrackedBytes: result.untrackedBytes
|
|
7470
|
+
};
|
|
7471
|
+
if (usage) {
|
|
7472
|
+
details.usage = usage;
|
|
7473
|
+
}
|
|
7474
|
+
const response = {
|
|
7475
|
+
content: [
|
|
7476
|
+
{
|
|
7477
|
+
text: `Advisor (${result.model})
|
|
7358
7478
|
|
|
7359
7479
|
${result.markdown}`,
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7480
|
+
type: "text"
|
|
7481
|
+
}
|
|
7482
|
+
],
|
|
7483
|
+
details
|
|
7484
|
+
};
|
|
7485
|
+
if (piUsage) {
|
|
7486
|
+
response.usage = piUsage;
|
|
7487
|
+
}
|
|
7488
|
+
return response;
|
|
7489
|
+
} catch (error) {
|
|
7490
|
+
coalescedUpdate.flush();
|
|
7491
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7492
|
+
session.recordInvocation({
|
|
7493
|
+
executionEffect: "continued",
|
|
7494
|
+
failure: "provider-error",
|
|
7495
|
+
kind: "markdown",
|
|
7496
|
+
model: advisorRef,
|
|
7497
|
+
trigger: "executor-requested"
|
|
7498
|
+
});
|
|
7499
|
+
updateAdvisorUsageStatus(ctx, session);
|
|
7500
|
+
notifyLocalFailure(ctx, message);
|
|
7501
|
+
notifyHerdrAdvisorFailure("Advisor consultation failed", message);
|
|
7502
|
+
throw error;
|
|
7503
|
+
} finally {
|
|
7504
|
+
finishHerdrActivity();
|
|
7505
|
+
coalescedUpdate.cancel();
|
|
7367
7506
|
}
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
coalescedUpdate.flush();
|
|
7371
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
7372
|
-
session.recordInvocation({
|
|
7373
|
-
executionEffect: "continued",
|
|
7374
|
-
failure: "provider-error",
|
|
7375
|
-
kind: "markdown",
|
|
7376
|
-
model: advisorRef,
|
|
7377
|
-
trigger: "executor-requested"
|
|
7378
|
-
});
|
|
7379
|
-
updateAdvisorUsageStatus(ctx, session);
|
|
7380
|
-
notifyLocalFailure(ctx, message);
|
|
7381
|
-
notifyHerdrAdvisorFailure("Advisor consultation failed", message);
|
|
7382
|
-
throw error;
|
|
7383
|
-
} finally {
|
|
7384
|
-
coalescedUpdate.cancel();
|
|
7385
|
-
herdrAdvisorActivity.finish();
|
|
7386
|
-
}
|
|
7507
|
+
};
|
|
7508
|
+
return runConsultation();
|
|
7387
7509
|
},
|
|
7388
7510
|
label: "Ask Advisor",
|
|
7389
7511
|
name: "ask_advisor",
|
|
@@ -7461,7 +7583,7 @@ var sendAutomaticGateResult = (pi, result) => {
|
|
|
7461
7583
|
display: true
|
|
7462
7584
|
}, { deliverAs: "steer" });
|
|
7463
7585
|
};
|
|
7464
|
-
var applyGateDecision = (pi, ctx, session, result, reason, failureMode) => {
|
|
7586
|
+
var applyGateDecision = (pi, ctx, session, result, reason, failureMode, herdrBlock) => {
|
|
7465
7587
|
if (!result.ok) {
|
|
7466
7588
|
session.recordInvocation({
|
|
7467
7589
|
executionEffect: gateFailureEffectForMode(failureMode),
|
|
@@ -7472,7 +7594,7 @@ var applyGateDecision = (pi, ctx, session, result, reason, failureMode) => {
|
|
|
7472
7594
|
usage: result.usage
|
|
7473
7595
|
});
|
|
7474
7596
|
updateAdvisorUsageStatus(ctx, session);
|
|
7475
|
-
const failure = failureEffect(result.category, result.message, ctx, session, failureMode);
|
|
7597
|
+
const failure = failureEffect(result.category, result.message, ctx, session, failureMode, herdrBlock);
|
|
7476
7598
|
sendAutomaticGateFailure(pi, `**Advisor gate failure (${result.category}):** ${result.message}`, snapshotAdvisorUsage(result.usage));
|
|
7477
7599
|
return failure.block ? { block: true, reason: `${reason}
|
|
7478
7600
|
${failure.reason}` } : undefined;
|
|
@@ -7494,24 +7616,24 @@ ${failure.reason}` } : undefined;
|
|
|
7494
7616
|
}
|
|
7495
7617
|
const gateReason = `Advisor loop review: ${result.markdown}`;
|
|
7496
7618
|
if (result.decision === "blocked") {
|
|
7497
|
-
const effect = blockedDecisionEffect(gateReason, ctx, session, failureMode);
|
|
7619
|
+
const effect = blockedDecisionEffect(gateReason, ctx, session, failureMode, herdrBlock);
|
|
7498
7620
|
return effect.block ? { block: true, reason: effect.reason } : undefined;
|
|
7499
7621
|
}
|
|
7500
7622
|
return { block: true, reason: gateReason };
|
|
7501
7623
|
};
|
|
7502
|
-
var handleAutomaticGate = async (pi, event, ctx, session, runGate, scoutStatus) => {
|
|
7624
|
+
var handleAutomaticGate = async (pi, event, ctx, session, runGate, scoutStatus, herdrActivity = herdrAdvisorActivity, herdrBlock = herdrAdvisorBlock) => {
|
|
7503
7625
|
if (isSimpleMode() || event.toolName === "ask_advisor" || !advisorModelIsAllowed(ctx) || !advisorAutoLoopGateRef || !session.recordToolCall(event.toolName, event.input, advisorLoopThresholdRef)) {
|
|
7504
7626
|
return;
|
|
7505
7627
|
}
|
|
7506
7628
|
const reason = `Advisor loop gate: normalized signature for ${event.toolName} repeated ${advisorLoopThresholdRef} times without a materially different tool action.`;
|
|
7507
7629
|
const failureMode = advisorFailureModeRef;
|
|
7508
7630
|
if (!session.canConsult(getAdvisorMaxCallsPerSession())) {
|
|
7509
|
-
const failure = failureEffect("budget-exhausted", "Advisor gate call budget is exhausted.", ctx, session, failureMode);
|
|
7631
|
+
const failure = failureEffect("budget-exhausted", "Advisor gate call budget is exhausted.", ctx, session, failureMode, herdrBlock);
|
|
7510
7632
|
return failure.block ? { block: true, reason: failure.reason } : undefined;
|
|
7511
7633
|
}
|
|
7512
7634
|
session.consumeCall();
|
|
7513
7635
|
session.resetTurnsSinceConsultation();
|
|
7514
|
-
|
|
7636
|
+
const finishHerdrActivity = herdrActivity.start();
|
|
7515
7637
|
let scoutDetails;
|
|
7516
7638
|
const scoutStatusToken = Symbol("automatic-gate-scout");
|
|
7517
7639
|
scoutStatus.register(scoutStatusToken);
|
|
@@ -7522,10 +7644,10 @@ var handleAutomaticGate = async (pi, event, ctx, session, runGate, scoutStatus)
|
|
|
7522
7644
|
gateCallSent = true;
|
|
7523
7645
|
}
|
|
7524
7646
|
};
|
|
7525
|
-
if (!advisorScoutEnabledRef) {
|
|
7526
|
-
ensureGateCall();
|
|
7527
|
-
}
|
|
7528
7647
|
try {
|
|
7648
|
+
if (!advisorScoutEnabledRef) {
|
|
7649
|
+
ensureGateCall();
|
|
7650
|
+
}
|
|
7529
7651
|
const result = await runGate(ctx, `${reason} Review the repeated actions and recommend the smallest safe next step.`, "repeated-tool-call", ctx.signal, undefined, (scoutEvent) => {
|
|
7530
7652
|
scoutStatus.update(ctx, scoutStatusToken, scoutEvent);
|
|
7531
7653
|
scoutDetails = appendScoutLifecycleEntry(pi, scoutEvent, scoutDetails);
|
|
@@ -7534,10 +7656,10 @@ var handleAutomaticGate = async (pi, event, ctx, session, runGate, scoutStatus)
|
|
|
7534
7656
|
}
|
|
7535
7657
|
}, event.toolCallId);
|
|
7536
7658
|
ensureGateCall();
|
|
7537
|
-
return applyGateDecision(pi, ctx, session, result, reason, failureMode);
|
|
7659
|
+
return applyGateDecision(pi, ctx, session, result, reason, failureMode, herdrBlock);
|
|
7538
7660
|
} finally {
|
|
7661
|
+
finishHerdrActivity();
|
|
7539
7662
|
scoutStatus.release(ctx, scoutStatusToken);
|
|
7540
|
-
herdrAdvisorActivity.finish();
|
|
7541
7663
|
}
|
|
7542
7664
|
};
|
|
7543
7665
|
|
|
@@ -7553,6 +7675,8 @@ var modelAccessBlock = (toolName, ctx) => {
|
|
|
7553
7675
|
};
|
|
7554
7676
|
};
|
|
7555
7677
|
var registerToolLifecycle = ({
|
|
7678
|
+
herdrActivity,
|
|
7679
|
+
herdrBlock,
|
|
7556
7680
|
pi,
|
|
7557
7681
|
reservedCalls,
|
|
7558
7682
|
runGate,
|
|
@@ -7579,7 +7703,7 @@ var registerToolLifecycle = ({
|
|
|
7579
7703
|
pi.on("session_start", (_event, ctx) => {
|
|
7580
7704
|
session.resetTask();
|
|
7581
7705
|
reservedCalls.clear();
|
|
7582
|
-
|
|
7706
|
+
herdrBlock.clear();
|
|
7583
7707
|
if (ctx?.hasUI) {
|
|
7584
7708
|
ctx.ui.setStatus("advisor-usage", undefined);
|
|
7585
7709
|
}
|
|
@@ -7627,10 +7751,11 @@ ${guidelines.map((rule) => `- ${rule}`).join(`
|
|
|
7627
7751
|
if (event.toolName === "ask_advisor") {
|
|
7628
7752
|
return reservation;
|
|
7629
7753
|
}
|
|
7630
|
-
return handleAutomaticGate(pi, event, ctx, session, runGate, scoutStatus);
|
|
7754
|
+
return handleAutomaticGate(pi, event, ctx, session, runGate, scoutStatus, herdrActivity, herdrBlock);
|
|
7631
7755
|
});
|
|
7632
7756
|
pi.on("agent_settled", (_event, ctx) => {
|
|
7633
7757
|
reservedCalls.clear();
|
|
7758
|
+
session.clearCallReservations();
|
|
7634
7759
|
if (isSimpleMode() || session.blocked || !advisorSessionSummaryRef) {
|
|
7635
7760
|
return;
|
|
7636
7761
|
}
|
|
@@ -7641,8 +7766,9 @@ ${guidelines.map((rule) => `- ${rule}`).join(`
|
|
|
7641
7766
|
});
|
|
7642
7767
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
7643
7768
|
reservedCalls.clear();
|
|
7769
|
+
session.clearCallReservations();
|
|
7644
7770
|
scoutStatus.clear(ctx);
|
|
7645
|
-
|
|
7771
|
+
herdrBlock.clear();
|
|
7646
7772
|
if (ctx?.hasUI) {
|
|
7647
7773
|
ctx.ui.setStatus("advisor-usage", undefined);
|
|
7648
7774
|
}
|
|
@@ -7776,6 +7902,8 @@ var registerAdvisorTool = (pi, session = advisorSessionState, dependencies = {})
|
|
|
7776
7902
|
const registration = {
|
|
7777
7903
|
appendOutcome: dependencies.appendOutcome ?? appendOutcome,
|
|
7778
7904
|
consult: dependencies.consult ?? consultAdvisor,
|
|
7905
|
+
herdrActivity: dependencies.herdrActivity ?? herdrAdvisorActivity,
|
|
7906
|
+
herdrBlock: dependencies.herdrBlock ?? herdrAdvisorBlock,
|
|
7779
7907
|
pi,
|
|
7780
7908
|
reservedCalls: new Set,
|
|
7781
7909
|
runGate: dependencies.runGate ?? runAdvisorGate,
|
|
@@ -7790,6 +7918,7 @@ var registerAdvisorTool = (pi, session = advisorSessionState, dependencies = {})
|
|
|
7790
7918
|
const turnGate = {
|
|
7791
7919
|
activeTools: () => pi.getActiveTools(),
|
|
7792
7920
|
consult: registration.consult,
|
|
7921
|
+
herdrActivity: registration.herdrActivity,
|
|
7793
7922
|
send: (message) => pi.sendMessage(message, { deliverAs: "steer" }),
|
|
7794
7923
|
session
|
|
7795
7924
|
};
|
|
@@ -7806,11 +7935,17 @@ var runAdvisorGate2 = (...args) => runAdvisorGate(...args);
|
|
|
7806
7935
|
function registerPiAdvisor(pi) {
|
|
7807
7936
|
const sessionState = new AdvisorSessionState;
|
|
7808
7937
|
const scoutStatus = new ScoutStatusManager;
|
|
7809
|
-
|
|
7938
|
+
const herdrActivity = herdrAdvisorActivity.createScope();
|
|
7939
|
+
const herdrBlock = new HerdrAdvisorBlock(undefined, () => getAdvisorSettings().herdrIntegration, (active, label) => {
|
|
7940
|
+
pi.events.emit("herdr:blocked", { active, label });
|
|
7941
|
+
});
|
|
7810
7942
|
registerAdvisorTool(pi, sessionState, {
|
|
7943
|
+
herdrActivity,
|
|
7944
|
+
herdrBlock,
|
|
7811
7945
|
statusManager: scoutStatus
|
|
7812
7946
|
});
|
|
7813
7947
|
registerCommands(pi, {
|
|
7948
|
+
herdrActivity,
|
|
7814
7949
|
sessionState,
|
|
7815
7950
|
statusManager: scoutStatus
|
|
7816
7951
|
});
|