brl-subagent 2.3.4 → 2.3.6
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/AGENT.md +58 -0
- package/README.md +11 -7
- package/package.json +3 -2
- package/src/index.ts +11 -37
- package/src/paths.ts +25 -0
- package/src/state.ts +0 -10
- package/src/templates.ts +2 -1
- package/src/tui.ts +0 -27
- package/src/types.ts +1 -13
- package/src/update.ts +0 -109
package/AGENT.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# AGENT.md — brl-subagent capability reference
|
|
2
|
+
|
|
3
|
+
> The `delegate_task` prompt guidelines are a derived summary of this file; if they disagree, this file wins.
|
|
4
|
+
|
|
5
|
+
This file is the authoritative reference for how the brl-subagent extension behaves
|
|
6
|
+
and how you (the conductor) should judge a delegation. Read it when planning
|
|
7
|
+
delegation-heavy work.
|
|
8
|
+
|
|
9
|
+
## Execution models
|
|
10
|
+
|
|
11
|
+
| Mode | What blocks | What wakes you | Batchable? |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| single, foreground | blocks until it returns | — (result comes back inline) | no |
|
|
14
|
+
| single, background | returns immediately with an agent id | yes — a `subagent-completion` message on terminal state | no |
|
|
15
|
+
| chain | blocks until the chain finishes or fails | — | yes (foreground) |
|
|
16
|
+
| parallel (`tasks`) | blocks until every task finishes | — | yes (foreground) |
|
|
17
|
+
| graph | blocks; runs in waves via `dependsOn` | — | yes (foreground) |
|
|
18
|
+
|
|
19
|
+
- Foreground single blocks: you wait for the result inline.
|
|
20
|
+
- Background single returns immediately with an agent id; a completion message wakes you (see below).
|
|
21
|
+
- Chain, parallel, and graph are batch — one call, many subtasks. They are foreground today; background batch is future work.
|
|
22
|
+
- Chain stops at the first failure; max 10 steps.
|
|
23
|
+
- Parallel runs every task regardless of the others — no short-circuit.
|
|
24
|
+
- Graph runs in waves, ordered by `dependsOn` edges.
|
|
25
|
+
|
|
26
|
+
## The completion contract
|
|
27
|
+
|
|
28
|
+
- A background run wakes you with a structured `subagent-completion` message when it reaches a terminal state (`completed`, `failed`, or `stopped`). The `details` carry `id`, `status`, `errorCategory` (with `errorMessage` when present), cost/tokens/duration, and `label`; the content carries a tail of output.
|
|
29
|
+
- The wake triggers a turn even when you are idle. Do not poll for it.
|
|
30
|
+
- Delivery mode by status holds at knob `"all"`: `failed`/`stopped` → steering you to act; `completed` → follow-up. The `completionNotify` knob changes both the delivery mode and the wake: under `"failed"` a `completed` run is delivered as a passive `nextTurn` (no wake) while `failed`/`stopped` still steer; under `"off"` everything is a passive `nextTurn` and nothing wakes you. (The knob controls whether an idle conductor is triggered — see below.)
|
|
31
|
+
- Knob dependency: polling is correct only when wakes are disabled (`completionNotify: "off"`). The wake is per-terminal-status and knob-scoped: under `"all"` a terminal run always wakes you; under `"failed"` only a `failed` or `stopped` run wakes you — a `completed` run stays a passive `nextTurn` (no wake); under `"off"` nothing wakes you. So polling is wrong unless wakes are disabled. One status check as a stall check is legitimate; repeated polling is not.
|
|
32
|
+
- One message per run — the extension deduplicates on the first terminal event for a run id, so don't expect multiple messages for a single run.
|
|
33
|
+
- Honest records: trust the message's `category`/abort source over your own guess. The provider/abort origin is authoritative on why the run stopped.
|
|
34
|
+
- Steering is not an abort. Being steered to act on a terminal run is not terminating it. A real `stop` is an abort — and a stopped run still wakes you.
|
|
35
|
+
|
|
36
|
+
## Delegation judgment
|
|
37
|
+
|
|
38
|
+
- Foreground vs background: if your next step needs the result inline, run foreground. If the user can keep working or is AFK, run background and act on the wake.
|
|
39
|
+
- Batch vs sequential: if the work is pre-declared, use chain/parallel/graph. If you're steering incrementally with the user, issue sequential calls.
|
|
40
|
+
- Retry taxonomy: `retryRunId` is a retry — it re-runs with the recorded task and params. A re-dispatch (a fresh call with the same task text) is not a retry. Never re-issue an identical spec after a termination without confirming the cause with the user (Rule 18).
|
|
41
|
+
- When to delegate: when the task needs an isolated context, a deep investigation, parallel research, or a long-running analysis.
|
|
42
|
+
- Approval: `approvalMode: "always"` is rejected in background — there is no dialog. Use `auto` (default) or `writes`; `writes` auto-approves in background.
|
|
43
|
+
|
|
44
|
+
## Canonical shapes
|
|
45
|
+
|
|
46
|
+
- Batch chain with `{previous}`: `chain: [{ task: "…" }, { task: "Use {previous} …" }]` — each step gets the prior step's output.
|
|
47
|
+
- Graph with `dependsOn`: subtasks declare edges; the scheduler runs them in waves.
|
|
48
|
+
- Background + act on wake: spawn a background run, then act when the completion message arrives. Never poll between spawn and wake.
|
|
49
|
+
- retryRunId: pass a failed run's id to re-run it with its recorded task and params; explicit parameters override the originals.
|
|
50
|
+
|
|
51
|
+
## Mechanics pointer
|
|
52
|
+
|
|
53
|
+
Presets and templates are documented in the schema, not here. Judgment only:
|
|
54
|
+
|
|
55
|
+
- Prefer a preset when the delegation matches a standard shape — review/audit/test/docs; override per-call deliberately.
|
|
56
|
+
- Recurring task shapes → a saved template.
|
|
57
|
+
|
|
58
|
+
The schema's `delegate_task` parameters, the `templateSummary` and `presetRestrictionSummary` lines in the prompt guidelines, and `get_subagent_result`'s description are the mechanics reference.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Multi-agent orchestration for [pi](https://github.com/earendil-works/pi) — chain, parallel, and dependency-graph delegation to isolated subagents with per-step model routing, preset-driven tool scoping, thinking-level control, and background execution with live monitoring, real abort, and per-agent timeouts.
|
|
4
4
|
|
|
5
|
-
**Version:** 2.3.
|
|
5
|
+
**Version:** 2.3.6 · **Author:** Azmeen Afandi / [Beeroo Labs](https://beeroolabs.com) · **License:** MIT
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- **True background execution** — live monitor, real abort (`stop_subagent`), per-agent timeouts, and hard caps. Nothing orphans; nothing leaks.
|
|
15
15
|
- **Preset-driven tool scoping** — every subagent runs with exactly the tools its job needs, restricted by preset or per-call `tools`/`excludeTools`, with auto-route that picks the right preset when you don't.
|
|
16
16
|
- **Templates with slots** — saved, file-backed task templates with `${param}` placeholders for workflows you run again and again.
|
|
17
|
-
- **Safety by default** — task-fence injection protection, sanitized error paths, owner-only persistence, and a
|
|
17
|
+
- **Safety by default** — task-fence injection protection, sanitized error paths, owner-only persistence, and a 975+ test suite pinning every contract against the real pi SDK.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -22,13 +22,18 @@
|
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
# Global (all projects)
|
|
25
|
-
|
|
25
|
+
pi install npm:brl-subagent
|
|
26
26
|
|
|
27
|
-
# Project-local
|
|
28
|
-
|
|
27
|
+
# Project-local (commit .pi/settings.json to share with your team)
|
|
28
|
+
pi install -l npm:brl-subagent
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Pi
|
|
31
|
+
Pi installs the package and loads the extension automatically. Updates and removal:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pi update --extensions # update installed packages
|
|
35
|
+
pi remove npm:brl-subagent # uninstall
|
|
36
|
+
```
|
|
32
37
|
|
|
33
38
|
---
|
|
34
39
|
|
|
@@ -55,7 +60,6 @@ Pi auto-discovers extensions in these directories. To update: `git pull` inside
|
|
|
55
60
|
| `/brl-subagent monitor` | Live monitor running subagents |
|
|
56
61
|
| `/brl-subagent dashboard` | Live observability dashboard |
|
|
57
62
|
| `/brl-subagent retry` | Browse failed runs to retry |
|
|
58
|
-
| `/brl-subagent update-check` | Toggle update check on startup |
|
|
59
63
|
| `/brl-subagent reset` | Reset all configuration |
|
|
60
64
|
|
|
61
65
|
All settings persist across sessions.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brl-subagent",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"description": "Subagent extension for pi with configurable model and thinking mode via selection UI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"templates/",
|
|
17
17
|
"README.md",
|
|
18
18
|
"LICENSE",
|
|
19
|
+
"AGENT.md",
|
|
19
20
|
"!src/__tests__/"
|
|
20
21
|
],
|
|
21
22
|
"pi": {
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"jiti": "^2.7.0",
|
|
42
43
|
"typebox": "^1.3.25",
|
|
43
44
|
"typescript": "^5.9.3",
|
|
44
|
-
"vitest": "^
|
|
45
|
+
"vitest": "^5.0.0"
|
|
45
46
|
},
|
|
46
47
|
"overrides": {
|
|
47
48
|
"nanoid": "^3.3.17"
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* See AGENT.md for the conductor-facing capability reference.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
7
|
import * as path from "node:path";
|
|
9
8
|
import type {
|
|
10
9
|
AgentToolResult,
|
|
@@ -52,6 +51,7 @@ import {
|
|
|
52
51
|
} from "./types";
|
|
53
52
|
import { validateGraph, topologicalSort } from "./scheduler";
|
|
54
53
|
import { resolveTemplate, loadAllTemplates, loadBuiltinTemplates, validateTemplatePresetRefs, extractParamNames } from "./templates";
|
|
54
|
+
import { pkgPath } from "./paths";
|
|
55
55
|
|
|
56
56
|
import { sanitizeTask, validateCwd, validateOutputFile, stripAnsi, capOutput, getCurrentDepth, sanitizeErrorMessage, buildCrashResult } from "./sanitize";
|
|
57
57
|
import {
|
|
@@ -92,7 +92,6 @@ import {
|
|
|
92
92
|
showCompletionNotifySelector,
|
|
93
93
|
showPresetManager,
|
|
94
94
|
showTemplateManager,
|
|
95
|
-
showUpdateCheckToggle,
|
|
96
95
|
showSLAConfig,
|
|
97
96
|
showSLAStats,
|
|
98
97
|
showConfigMenu,
|
|
@@ -105,8 +104,6 @@ import {
|
|
|
105
104
|
} from "./tui";
|
|
106
105
|
import { createLogger, type Logger } from "./logging";
|
|
107
106
|
import { Intercom } from "./messaging";
|
|
108
|
-
import { checkForUpdates } from "./update";
|
|
109
|
-
import { UPDATE_CHECK_INTERVAL_MS } from "./types";
|
|
110
107
|
import * as eventBus from "./event-bus";
|
|
111
108
|
import {
|
|
112
109
|
buildCompletionMessage,
|
|
@@ -197,16 +194,6 @@ export function buildTemplateGuideline(templateSummary: string): string {
|
|
|
197
194
|
export default function (pi: ExtensionAPI) {
|
|
198
195
|
const log = createLogger("brl-subagent");
|
|
199
196
|
|
|
200
|
-
// Read current version from package.json
|
|
201
|
-
const currentVersion = (() => {
|
|
202
|
-
try {
|
|
203
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"));
|
|
204
|
-
return pkg.version || "0.0.0";
|
|
205
|
-
} catch {
|
|
206
|
-
return "0.0.0";
|
|
207
|
-
}
|
|
208
|
-
})();
|
|
209
|
-
|
|
210
197
|
// F7: Session-bound state — initialized per session
|
|
211
198
|
let state = createSessionState(log);
|
|
212
199
|
|
|
@@ -1931,7 +1918,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1931
1918
|
preset: () => showPresetManager(ctx, state),
|
|
1932
1919
|
templates: () => showTemplateManager(ctx, state),
|
|
1933
1920
|
retry: () => showRetryMenu(ctx, state),
|
|
1934
|
-
"update-check": () => showUpdateCheckToggle(ctx, state, applyConfig),
|
|
1935
1921
|
sla: () => showSLAConfig(ctx, state, applyConfig),
|
|
1936
1922
|
"sla-stats": () => showSLAStats(ctx, state),
|
|
1937
1923
|
};
|
|
@@ -1954,7 +1940,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1954
1940
|
|
|
1955
1941
|
// B1: Load built-in presets once at registration to expose their tool
|
|
1956
1942
|
// restrictions to the conductor before any delegate_task call happens.
|
|
1957
|
-
const registrationPresets = loadBuiltinPresets(
|
|
1943
|
+
const registrationPresets = loadBuiltinPresets(pkgPath("presets"));
|
|
1958
1944
|
const presetRestrictionSummary = buildInventorySummary(registrationPresets, (p) => {
|
|
1959
1945
|
const restricted = p.excludeTools?.length || p.tools?.length;
|
|
1960
1946
|
if (!restricted) return p.name;
|
|
@@ -1965,9 +1951,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
1965
1951
|
// inventory (name + purpose + ${param} slots) to the conductor before any
|
|
1966
1952
|
// delegate_task call. Custom/user templates can't be enumerated statically,
|
|
1967
1953
|
// so the summary covers built-ins only and points at /brl-subagent templates.
|
|
1968
|
-
const registrationTemplates = loadBuiltinTemplates(
|
|
1954
|
+
const registrationTemplates = loadBuiltinTemplates(pkgPath("templates"));
|
|
1969
1955
|
const templateSummary = buildInventorySummary(registrationTemplates, formatTemplateSummaryItem);
|
|
1970
1956
|
|
|
1957
|
+
// Issue #158: resolve the shipped AGENT.md from the extension's own directory
|
|
1958
|
+
// rather than a hardcoded sync-copy destination, so npm-installed packages
|
|
1959
|
+
// (installed via `pi install npm:brl-subagent`) can find it.
|
|
1960
|
+
const agentMdPath = pkgPath("AGENT.md");
|
|
1961
|
+
|
|
1971
1962
|
pi.registerTool({
|
|
1972
1963
|
name: "delegate_task",
|
|
1973
1964
|
label: "Delegate Task",
|
|
@@ -1983,7 +1974,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1983
1974
|
"Delegate tasks to a subagent for isolated, parallel or background work",
|
|
1984
1975
|
promptGuidelines: [
|
|
1985
1976
|
"Use delegate_task when the user asks you to hand off work to a subagent, or when a task would benefit from an isolated context window (e.g., deep investigation, parallel research, long-running analysis).",
|
|
1986
|
-
|
|
1977
|
+
`The authoritative capability reference for delegation is the extension's AGENT.md — read it at ${agentMdPath}, or ./AGENT.md in the project root when present, when planning delegation-heavy work.`,
|
|
1987
1978
|
"The subagent inherits your system prompt and runs with its own model (configurable via /brl-subagent). It reports what it did when done.",
|
|
1988
1979
|
"You can customize per-call via inheritSystemPrompt and systemPrompt: set inheritSystemPrompt: false to save context, provide a systemPrompt for custom instructions, or use both to add instructions on top of inheritance.",
|
|
1989
1980
|
"Set thinkingLevel per call to match task complexity. The level is capped at the user's configured maximum. Map tasks to levels using this heuristic: off = file listing, grep, simple read. minimal = file diff, syntax check, find-and-replace. low = refactoring, test generation, documentation. medium = default — code review, debugging, moderate analysis. high = security audit, architecture review, complex debugging. xhigh = multi-step causal reasoning, research, novel problem solving. Default to 'off' or 'minimal' for trivial tasks — do not waste the user's budget.",
|
|
@@ -3691,11 +3682,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
3691
3682
|
// Issue #147: capture the session context for the completion-push subscriber.
|
|
3692
3683
|
sessionCtx = ctx;
|
|
3693
3684
|
// Load built-in presets
|
|
3694
|
-
const presetsDir =
|
|
3685
|
+
const presetsDir = pkgPath("presets");
|
|
3695
3686
|
state.builtinPresets = loadBuiltinPresets(presetsDir, log);
|
|
3696
3687
|
state.customPresets = loadCustomPresets(ctx.cwd, log);
|
|
3697
3688
|
// Builtin templates + merged full stack (custom overrides builtin)
|
|
3698
|
-
const templatesDir =
|
|
3689
|
+
const templatesDir = pkgPath("templates");
|
|
3699
3690
|
state.builtinTemplates = loadBuiltinTemplates(templatesDir, log);
|
|
3700
3691
|
state.config.templates = loadAllTemplates(ctx.cwd, log, templatesDir);
|
|
3701
3692
|
|
|
@@ -3734,23 +3725,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
3734
3725
|
// F5/F9: Safe state restoration with type guards
|
|
3735
3726
|
state.restoreFromSession(ctx);
|
|
3736
3727
|
|
|
3737
|
-
// Check for updates (non-blocking, once per 24h)
|
|
3738
|
-
if (state.config.updateCheckEnabled) {
|
|
3739
|
-
const now = Date.now();
|
|
3740
|
-
if (now - state.config.lastUpdateCheck > UPDATE_CHECK_INTERVAL_MS) {
|
|
3741
|
-
state.config.lastUpdateCheck = now;
|
|
3742
|
-
checkForUpdates(currentVersion, log).then((result) => {
|
|
3743
|
-
if (result?.available) {
|
|
3744
|
-
ctx.ui.notify(
|
|
3745
|
-
"brl-subagent " + result.version + " available (current: " + currentVersion + "). Visit " + result.url + " to update. /brl-subagent update-check to disable.",
|
|
3746
|
-
"info"
|
|
3747
|
-
);
|
|
3748
|
-
log.info("Update available", { current: currentVersion, latest: result.version });
|
|
3749
|
-
}
|
|
3750
|
-
}).catch(() => {}); // silently ignore errors
|
|
3751
|
-
}
|
|
3752
|
-
}
|
|
3753
|
-
|
|
3754
3728
|
updateStatus(state, ctx);
|
|
3755
3729
|
});
|
|
3756
3730
|
|
package/src/paths.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* brl-subagent — Package-root path resolution
|
|
3
|
+
*
|
|
4
|
+
* The extension is loaded from `<pkg>/src/` (package.json `pi.extensions`
|
|
5
|
+
* points at `./src/index.ts`), so every module in this package lives directly
|
|
6
|
+
* in `src/` and the package root is exactly one level up. `pkgPath()` is the
|
|
7
|
+
* single helper for resolving bundled assets against that root — the same base
|
|
8
|
+
* used for `presets/`, `templates/`, `package.json`, and `AGENT.md`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolve `segments` against the package root.
|
|
15
|
+
*
|
|
16
|
+
* Because the extension loads from `<pkg>/src/`, the package root is one level
|
|
17
|
+
* up from this module's directory. A wrong depth here would resolve inside
|
|
18
|
+
* `src/` and fail to find the bundled asset.
|
|
19
|
+
*
|
|
20
|
+
* @param segments - Path segments below the package root (e.g. `"presets"`, `"AGENT.md"`)
|
|
21
|
+
* @returns The absolute path to the bundled asset
|
|
22
|
+
*/
|
|
23
|
+
export function pkgPath(...segments: string[]): string {
|
|
24
|
+
return path.join(__dirname, "..", ...segments);
|
|
25
|
+
}
|
package/src/state.ts
CHANGED
|
@@ -142,8 +142,6 @@ export class SessionState {
|
|
|
142
142
|
circuitBreaker: this.defaultCircuitBreaker(),
|
|
143
143
|
slaTrackingEnabled: false,
|
|
144
144
|
slaWindowSize: 50,
|
|
145
|
-
updateCheckEnabled: true,
|
|
146
|
-
lastUpdateCheck: 0,
|
|
147
145
|
};
|
|
148
146
|
}
|
|
149
147
|
|
|
@@ -168,8 +166,6 @@ export class SessionState {
|
|
|
168
166
|
slaTrackingEnabled: this.config.slaTrackingEnabled,
|
|
169
167
|
slaWindowSize: this.config.slaWindowSize,
|
|
170
168
|
lastSLAMetrics: this.config.lastSLAMetrics,
|
|
171
|
-
updateCheckEnabled: this.config.updateCheckEnabled,
|
|
172
|
-
lastUpdateCheck: this.config.lastUpdateCheck,
|
|
173
169
|
});
|
|
174
170
|
}
|
|
175
171
|
|
|
@@ -258,10 +254,6 @@ export class SessionState {
|
|
|
258
254
|
this.config.lastSLAMetrics = data.lastSLAMetrics as import("./types").SLAMetrics;
|
|
259
255
|
}
|
|
260
256
|
|
|
261
|
-
// Restore update check fields
|
|
262
|
-
if (typeof data.updateCheckEnabled === "boolean") this.config.updateCheckEnabled = data.updateCheckEnabled;
|
|
263
|
-
if (typeof data.lastUpdateCheck === "number" && data.lastUpdateCheck >= 0) this.config.lastUpdateCheck = data.lastUpdateCheck;
|
|
264
|
-
|
|
265
257
|
this.log?.info("State restored from session", {
|
|
266
258
|
model: data.model ? `${data.model.provider}/${data.model.id}` : "none",
|
|
267
259
|
thinkingLevel: data.maxThinkingLevel,
|
|
@@ -508,8 +500,6 @@ export class SessionState {
|
|
|
508
500
|
this.config.slaTrackingEnabled = false;
|
|
509
501
|
this.config.slaWindowSize = 50;
|
|
510
502
|
this.config.lastSLAMetrics = undefined;
|
|
511
|
-
this.config.updateCheckEnabled = true;
|
|
512
|
-
this.config.lastUpdateCheck = 0;
|
|
513
503
|
}
|
|
514
504
|
}
|
|
515
505
|
|
package/src/templates.ts
CHANGED
|
@@ -27,6 +27,7 @@ import type { TaskTemplate, ThinkingLevel, SubagentPreset } from "./types";
|
|
|
27
27
|
import { TEMPLATE_PARAM_RE, THINKING_LEVELS } from "./types";
|
|
28
28
|
import type { Logger } from "./logging";
|
|
29
29
|
import { parseFrontmatter, sanitizeFileName } from "./presets";
|
|
30
|
+
import { pkgPath } from "./paths";
|
|
30
31
|
|
|
31
32
|
// ---------------------------------------------------------------------------
|
|
32
33
|
// Param extraction
|
|
@@ -373,7 +374,7 @@ export function getAllTemplates(
|
|
|
373
374
|
* the extension's presets/ dir.
|
|
374
375
|
*/
|
|
375
376
|
export function loadAllTemplates(cwd: string, log?: Logger, templatesDir?: string): TaskTemplate[] {
|
|
376
|
-
const builtinDir = templatesDir ??
|
|
377
|
+
const builtinDir = templatesDir ?? pkgPath("templates");
|
|
377
378
|
return getAllTemplates(loadBuiltinTemplates(builtinDir, log), loadCustomTemplates(cwd, log));
|
|
378
379
|
}
|
|
379
380
|
|
package/src/tui.ts
CHANGED
|
@@ -337,28 +337,6 @@ export async function showCompletionNotifySelector(
|
|
|
337
337
|
onConfigChanged(ctx, `Completion notify mode set to ${result}`);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
// ---------------------------------------------------------------------------
|
|
341
|
-
// Update check toggle
|
|
342
|
-
// ---------------------------------------------------------------------------
|
|
343
|
-
|
|
344
|
-
export async function showUpdateCheckToggle(
|
|
345
|
-
ctx: ExtensionContext,
|
|
346
|
-
state: SessionState,
|
|
347
|
-
onConfigChanged: (ctx: ExtensionContext, msg: string) => void,
|
|
348
|
-
): Promise<void> {
|
|
349
|
-
const items: SelectItem[] = [
|
|
350
|
-
{ value: "enabled", label: "Enabled", description: "Check for updates on session start" },
|
|
351
|
-
{ value: "disabled", label: "Disabled", description: "Skip update check" },
|
|
352
|
-
];
|
|
353
|
-
|
|
354
|
-
const result = await showSelectList(ctx, "Update Check", items, 5);
|
|
355
|
-
if (!result) return;
|
|
356
|
-
|
|
357
|
-
state.config.updateCheckEnabled = result === "enabled";
|
|
358
|
-
onConfigChanged(ctx, `Update check ${result}`);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// ---------------------------------------------------------------------------
|
|
362
340
|
// ---------------------------------------------------------------------------
|
|
363
341
|
// E4: SLA tracking configuration
|
|
364
342
|
// ---------------------------------------------------------------------------
|
|
@@ -1199,11 +1177,6 @@ export function getConfigMenuItems(state: SessionState): SelectItem[] {
|
|
|
1199
1177
|
? `Enabled (window: ${state.config.slaWindowSize})`
|
|
1200
1178
|
: "Disabled",
|
|
1201
1179
|
},
|
|
1202
|
-
{
|
|
1203
|
-
value: "update-check",
|
|
1204
|
-
label: "Update Check",
|
|
1205
|
-
description: state.config.updateCheckEnabled ? "Enabled" : "Disabled",
|
|
1206
|
-
},
|
|
1207
1180
|
];
|
|
1208
1181
|
}
|
|
1209
1182
|
|
package/src/types.ts
CHANGED
|
@@ -209,8 +209,6 @@ export interface SubagentState {
|
|
|
209
209
|
slaTrackingEnabled: boolean; // E4: SLA tracking toggle
|
|
210
210
|
slaWindowSize: number; // E4: number of recent runs to analyze (10-500)
|
|
211
211
|
lastSLAMetrics?: SLAMetrics; // E4: persisted baseline for degradation comparison
|
|
212
|
-
updateCheckEnabled: boolean; // Version update notifier toggle
|
|
213
|
-
lastUpdateCheck: number; // Epoch ms of last update check
|
|
214
212
|
}
|
|
215
213
|
|
|
216
214
|
/**
|
|
@@ -556,8 +554,6 @@ export const TEMPLATE_PARAM_RE = /\$\{(\w+)\}/g;
|
|
|
556
554
|
|
|
557
555
|
export const DEFAULT_SESSION_COST_LIMIT = 0;
|
|
558
556
|
|
|
559
|
-
export const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
560
|
-
|
|
561
557
|
export const CUSTOM_ENTRY_TYPES = {
|
|
562
558
|
run: "brl-subagent-run",
|
|
563
559
|
state: "brl-subagent-state",
|
|
@@ -589,7 +585,7 @@ export const RESERVED_NAME_PATTERN = /^__.*__$/;
|
|
|
589
585
|
export const RESERVED_COMMAND_NAMES = new Set([
|
|
590
586
|
"model", "thinking", "concurrency", "depth", "history", "monitor",
|
|
591
587
|
"preset", "retry", "reset", "priority", "templates", "dashboard", "approval",
|
|
592
|
-
"costlimit", "historyentries", "sla", "
|
|
588
|
+
"costlimit", "historyentries", "sla", "completionnotify",
|
|
593
589
|
"graph", "sla-stats",
|
|
594
590
|
]);
|
|
595
591
|
|
|
@@ -750,14 +746,6 @@ export function isSubagentStateShape(value: unknown): value is SubagentState {
|
|
|
750
746
|
if (typeof v.slaWindowSize !== "number" || v.slaWindowSize < 10 || v.slaWindowSize > 500) return false;
|
|
751
747
|
}
|
|
752
748
|
|
|
753
|
-
// updateCheckEnabled must be a boolean if present
|
|
754
|
-
if (v.updateCheckEnabled !== undefined && typeof v.updateCheckEnabled !== "boolean") return false;
|
|
755
|
-
|
|
756
|
-
// lastUpdateCheck must be a non-negative number if present
|
|
757
|
-
if (v.lastUpdateCheck !== undefined) {
|
|
758
|
-
if (typeof v.lastUpdateCheck !== "number" || v.lastUpdateCheck < 0) return false;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
749
|
return true;
|
|
762
750
|
}
|
|
763
751
|
|
package/src/update.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* brl-subagent — Version Update Notifier
|
|
3
|
-
*
|
|
4
|
-
* Checks GitHub releases for newer versions of brl-subagent.
|
|
5
|
-
* Runs non-blocking with a 5-second timeout on session start.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { Logger } from "./logging";
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Types
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
export interface UpdateCheckResult {
|
|
15
|
-
available: boolean;
|
|
16
|
-
version?: string;
|
|
17
|
-
url?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Semver comparison
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Compare two semver strings (e.g., "2.1.0" vs "2.0.1").
|
|
26
|
-
* Returns 1 if a > b, -1 if a < b, 0 if equal.
|
|
27
|
-
*/
|
|
28
|
-
function compareSemver(a: string, b: string): number {
|
|
29
|
-
const aParts = a.split(".").map(Number);
|
|
30
|
-
const bParts = b.split(".").map(Number);
|
|
31
|
-
const len = Math.max(aParts.length, bParts.length);
|
|
32
|
-
|
|
33
|
-
for (let i = 0; i < len; i++) {
|
|
34
|
-
const aVal = aParts[i] ?? 0;
|
|
35
|
-
const bVal = bParts[i] ?? 0;
|
|
36
|
-
if (aVal > bVal) return 1;
|
|
37
|
-
if (aVal < bVal) return -1;
|
|
38
|
-
}
|
|
39
|
-
return 0;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// Main export
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Check GitHub for a newer release of brl-subagent.
|
|
48
|
-
*
|
|
49
|
-
* @param currentVersion - The current version string (e.g., "2.0.1")
|
|
50
|
-
* @param log - Optional logger for debug output
|
|
51
|
-
* @returns Update check result, or null on any error (never throws)
|
|
52
|
-
*/
|
|
53
|
-
export async function checkForUpdates(
|
|
54
|
-
currentVersion: string,
|
|
55
|
-
log?: Logger,
|
|
56
|
-
): Promise<UpdateCheckResult | null> {
|
|
57
|
-
try {
|
|
58
|
-
const controller = new AbortController();
|
|
59
|
-
const timeoutId = setTimeout(() => controller.abort(), 5_000);
|
|
60
|
-
|
|
61
|
-
const response = await fetch(
|
|
62
|
-
"https://api.github.com/repos/azmeenafandi/brl-subagent/releases/latest",
|
|
63
|
-
{
|
|
64
|
-
headers: {
|
|
65
|
-
Accept: "application/vnd.github+json",
|
|
66
|
-
"User-Agent": "brl-subagent",
|
|
67
|
-
},
|
|
68
|
-
signal: controller.signal,
|
|
69
|
-
},
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
clearTimeout(timeoutId);
|
|
73
|
-
|
|
74
|
-
if (!response.ok) {
|
|
75
|
-
log?.debug("Update check: non-OK response", { status: response.status });
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const data = (await response.json()) as {
|
|
80
|
-
tag_name?: string;
|
|
81
|
-
html_url?: string;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
if (!data.tag_name || !data.html_url) {
|
|
85
|
-
log?.debug("Update check: missing tag_name or html_url in response");
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Strip "v" prefix from tag_name (e.g., "v2.1.0" → "2.1.0")
|
|
90
|
-
const latestVersion = data.tag_name.replace(/^v/, "");
|
|
91
|
-
|
|
92
|
-
const cmp = compareSemver(latestVersion, currentVersion);
|
|
93
|
-
|
|
94
|
-
if (cmp > 0) {
|
|
95
|
-
return {
|
|
96
|
-
available: true,
|
|
97
|
-
version: latestVersion,
|
|
98
|
-
url: data.html_url,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return { available: false };
|
|
103
|
-
} catch (err) {
|
|
104
|
-
log?.debug("Update check failed", {
|
|
105
|
-
error: err instanceof Error ? err.message : String(err),
|
|
106
|
-
});
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|