@pellux/goodvibes-tui 0.19.86 → 0.19.88
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 +10 -0
- package/README.md +3 -3
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/runtime/bootstrap-core.ts +3 -1
- package/src/runtime/bootstrap.ts +14 -1
- package/src/tools/wrfc-agent-guard.ts +140 -88
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.19.88] — 2026-05-10
|
|
8
|
+
|
|
9
|
+
### Changes
|
|
10
|
+
- 3de00981 fix: preserve WRFC implementation scope
|
|
11
|
+
|
|
12
|
+
## [0.19.87] — 2026-05-09
|
|
13
|
+
|
|
14
|
+
### Changes
|
|
15
|
+
- e8f6269e fix: defer wrfc topology to sdk 0.33.22
|
|
16
|
+
|
|
7
17
|
## [0.19.86] — 2026-05-09
|
|
8
18
|
|
|
9
19
|
### Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/mgd34msu/goodvibes-tui)
|
|
6
6
|
|
|
7
7
|
A terminal-native AI coding, operations, automation, knowledge, and integration console with a typed runtime, omnichannel surfaces, structured memory/knowledge, and a raw ANSI renderer.
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ GoodVibes is a Bun program. Install Bun first and make sure `bun` is on `PATH`,
|
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
bun add -g @pellux/goodvibes-tui
|
|
21
|
-
bun pm trust -g @pellux/goodvibes-tui core-js tree-sitter-css tree-sitter-javascript tree-sitter-json tree-sitter-python tree-sitter-typescript
|
|
21
|
+
bun pm trust -g @pellux/goodvibes-tui @pellux/goodvibes-sdk core-js tree-sitter-css tree-sitter-javascript tree-sitter-json tree-sitter-python tree-sitter-typescript
|
|
22
22
|
goodvibes
|
|
23
23
|
```
|
|
24
24
|
|
|
@@ -53,7 +53,7 @@ Release distribution:
|
|
|
53
53
|
|
|
54
54
|
- GitHub Releases are the primary distribution path for compiled binaries
|
|
55
55
|
- `bun add -g @pellux/goodvibes-tui` is the recommended global install path; the package is hosted on the npm registry and Bun installs from that registry directly
|
|
56
|
-
- Bun global installs require trusting `@pellux/goodvibes-tui` so the package postinstall can download the matching TUI and daemon binaries; the dependency lifecycle packages currently requiring trust are `core-js`, `tree-sitter-css`, `tree-sitter-javascript`, `tree-sitter-json`, `tree-sitter-python`, and `tree-sitter-typescript`
|
|
56
|
+
- Bun global installs require trusting `@pellux/goodvibes-tui` so the package postinstall can download the matching TUI and daemon binaries; the dependency lifecycle packages currently requiring trust are `@pellux/goodvibes-sdk`, `core-js`, `tree-sitter-css`, `tree-sitter-javascript`, `tree-sitter-json`, `tree-sitter-python`, and `tree-sitter-typescript`
|
|
57
57
|
- `npm install -g @pellux/goodvibes-tui` is supported on Linux, macOS, and WSL when Bun is already installed; the preinstall check verifies Bun, and the install script downloads the matching TUI and daemon binaries for the current platform
|
|
58
58
|
- native Windows is not supported; use WSL on Windows
|
|
59
59
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.88",
|
|
4
4
|
"description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.ts",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"@anthropic-ai/vertex-sdk": "^0.16.0",
|
|
98
98
|
"@ast-grep/napi": "^0.42.0",
|
|
99
99
|
"@aws/bedrock-token-generator": "^1.1.0",
|
|
100
|
-
"@pellux/goodvibes-sdk": "0.33.
|
|
100
|
+
"@pellux/goodvibes-sdk": "0.33.22",
|
|
101
101
|
"bash-language-server": "^5.6.0",
|
|
102
102
|
"fuse.js": "^7.1.0",
|
|
103
103
|
"graphql": "^16.13.2",
|
|
@@ -222,7 +222,9 @@ export async function initializeBootstrapCore(
|
|
|
222
222
|
overflowHandler: services.overflowHandler,
|
|
223
223
|
changeTracker: services.sessionChangeTracker,
|
|
224
224
|
});
|
|
225
|
-
installWrfcAgentToolGuard(toolRegistry
|
|
225
|
+
installWrfcAgentToolGuard(toolRegistry, {
|
|
226
|
+
getLastUserMessage: () => conversation.getLastUserMessage(),
|
|
227
|
+
});
|
|
226
228
|
services.agentOrchestrator.setDependencies({
|
|
227
229
|
surfaceRoot: 'tui',
|
|
228
230
|
fileCache,
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -49,6 +49,19 @@ import { createSafeHostServeFactory } from '../daemon/safe-serve.ts';
|
|
|
49
49
|
|
|
50
50
|
type ExternalServiceFactories = NonNullable<Parameters<typeof startExternalServices>[4]>;
|
|
51
51
|
|
|
52
|
+
const TUI_ORCHESTRATION_GUARDRAILS = [
|
|
53
|
+
'## GoodVibes TUI Orchestration Guardrails',
|
|
54
|
+
'- If the user asks to make, build, implement, create, add, fix, update, or patch something, preserve that implementation request. Do not restate it as design-only, planning-only, read-only, or no-write work unless the user explicitly requested that.',
|
|
55
|
+
'- Do not add "Do not write files", restrict tools to read/find/inspect, or remove write/exec capability for implementation work unless the user explicitly asked for read-only analysis.',
|
|
56
|
+
'- For one deliverable that needs WRFC, reviewed implementation, testing, verification, or review/fix cycles, use one `agent` spawn with `template: "engineer"` and `reviewMode: "wrfc"` whose `task` is the full user request. Do not batch-spawn sibling Engineer/Reviewer/Tester/Verifier roots for the same deliverable.',
|
|
57
|
+
'- Use `batch-spawn` only for genuinely independent sidecar tasks. Review, test, verify, and fix phases for one deliverable belong inside the WRFC owner chain.',
|
|
58
|
+
'- If an `agent` tool result reports `authoritativeWrfcChain: true`, `continueRootSpawning: false`, or `orchestrationStopSignal: "wrfc_owner_chain_started"`, stop spawning root agents for that deliverable and wait/report status instead.',
|
|
59
|
+
].join('\n');
|
|
60
|
+
|
|
61
|
+
function joinPromptParts(...parts: Array<string | null | undefined>): string {
|
|
62
|
+
return parts.map((part) => part?.trim()).filter((part): part is string => Boolean(part)).join('\n\n');
|
|
63
|
+
}
|
|
64
|
+
|
|
52
65
|
// ── Bootstrap context type ──────────────────────────────────────────────────
|
|
53
66
|
|
|
54
67
|
/**
|
|
@@ -196,7 +209,7 @@ export async function bootstrapRuntime(
|
|
|
196
209
|
const contextWindow = providerRegistry.getContextWindowForModel(currentModel);
|
|
197
210
|
const tier = getTierForContextWindow(contextWindow);
|
|
198
211
|
const supplement = getTierPromptSupplement(tier);
|
|
199
|
-
return
|
|
212
|
+
return joinPromptParts(runtime.systemPrompt, TUI_ORCHESTRATION_GUARDRAILS, supplement);
|
|
200
213
|
},
|
|
201
214
|
hookDispatcher,
|
|
202
215
|
flagManager: services.featureFlags,
|
|
@@ -5,6 +5,17 @@ type AgentToolArgs = {
|
|
|
5
5
|
readonly mode?: unknown;
|
|
6
6
|
readonly task?: unknown;
|
|
7
7
|
readonly template?: unknown;
|
|
8
|
+
readonly tools?: unknown;
|
|
9
|
+
readonly restrictTools?: unknown;
|
|
10
|
+
readonly context?: unknown;
|
|
11
|
+
readonly successCriteria?: unknown;
|
|
12
|
+
readonly requiredEvidence?: unknown;
|
|
13
|
+
readonly writeScope?: unknown;
|
|
14
|
+
readonly executionProtocol?: unknown;
|
|
15
|
+
readonly model?: unknown;
|
|
16
|
+
readonly provider?: unknown;
|
|
17
|
+
readonly fallbackModels?: unknown;
|
|
18
|
+
readonly cohort?: unknown;
|
|
8
19
|
readonly reviewMode?: unknown;
|
|
9
20
|
readonly dangerously_disable_wrfc?: unknown;
|
|
10
21
|
readonly tasks?: unknown;
|
|
@@ -14,62 +25,67 @@ type AgentToolArgs = {
|
|
|
14
25
|
type AgentTaskArgs = {
|
|
15
26
|
readonly task?: unknown;
|
|
16
27
|
readonly template?: unknown;
|
|
28
|
+
readonly tools?: unknown;
|
|
29
|
+
readonly restrictTools?: unknown;
|
|
30
|
+
readonly context?: unknown;
|
|
31
|
+
readonly successCriteria?: unknown;
|
|
32
|
+
readonly requiredEvidence?: unknown;
|
|
33
|
+
readonly writeScope?: unknown;
|
|
34
|
+
readonly executionProtocol?: unknown;
|
|
35
|
+
readonly model?: unknown;
|
|
36
|
+
readonly provider?: unknown;
|
|
37
|
+
readonly fallbackModels?: unknown;
|
|
17
38
|
readonly reviewMode?: unknown;
|
|
18
39
|
readonly dangerously_disable_wrfc?: unknown;
|
|
19
40
|
readonly [key: string]: unknown;
|
|
20
41
|
};
|
|
21
42
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'verify ',
|
|
28
|
-
'verify:',
|
|
29
|
-
'verify the ',
|
|
30
|
-
'test ',
|
|
31
|
-
'test:',
|
|
32
|
-
'test the ',
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
export function installWrfcAgentToolGuard(registry: ToolRegistry): void {
|
|
43
|
+
type WrfcAgentToolGuardOptions = {
|
|
44
|
+
readonly getLastUserMessage?: () => string | null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function installWrfcAgentToolGuard(registry: ToolRegistry, options: WrfcAgentToolGuardOptions = {}): void {
|
|
36
48
|
const agentTool = registry.list().find((tool) => tool.definition.name === 'agent');
|
|
37
49
|
if (!agentTool) throw new Error('WRFC agent guard could not find the agent tool.');
|
|
38
|
-
wrapWrfcAgentTool(agentTool);
|
|
50
|
+
wrapWrfcAgentTool(agentTool, options);
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
export function wrapWrfcAgentTool(tool: Tool): void {
|
|
53
|
+
export function wrapWrfcAgentTool(tool: Tool, options: WrfcAgentToolGuardOptions = {}): void {
|
|
42
54
|
const originalExecute = tool.execute.bind(tool);
|
|
43
55
|
tool.execute = async (args) => {
|
|
44
56
|
const denial = validateWrfcAgentToolInvocation(args as AgentToolArgs);
|
|
45
57
|
if (denial) return { success: false, error: denial };
|
|
46
|
-
return originalExecute(normalizeWrfcAgentToolInvocation(args as AgentToolArgs) as Parameters<Tool['execute']>[0]);
|
|
58
|
+
return originalExecute(normalizeWrfcAgentToolInvocation(args as AgentToolArgs, options) as Parameters<Tool['execute']>[0]);
|
|
47
59
|
};
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
export function validateWrfcAgentToolInvocation(args: AgentToolArgs): string | null {
|
|
51
|
-
if (args.mode
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'WRFC spawn blocked: a WRFC root task must be an owner/engineer task, not a reviewer/verifier/tester task.',
|
|
55
|
-
'Spawn one engineer/general owner with reviewMode:"wrfc"; WRFC creates reviewer and fixer agents only after owner output exists.',
|
|
56
|
-
].join(' ');
|
|
57
|
-
}
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (args.mode !== 'batch-spawn') return null;
|
|
63
|
+
if (args.mode !== 'spawn' && args.mode !== 'batch-spawn') return null;
|
|
64
|
+
// SDK owns WRFC topology enforcement. TUI must not block reviewer/tester/
|
|
65
|
+
// verifier root requests because the SDK normalizes those into owner chains.
|
|
62
66
|
return null;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
export function normalizeWrfcAgentToolInvocation(args: AgentToolArgs): AgentToolArgs {
|
|
69
|
+
export function normalizeWrfcAgentToolInvocation(args: AgentToolArgs, options: WrfcAgentToolGuardOptions = {}): AgentToolArgs {
|
|
70
|
+
const lastUserMessage = cleanText(options.getLastUserMessage?.() ?? null);
|
|
66
71
|
if (args.mode === 'spawn') {
|
|
67
|
-
if (
|
|
72
|
+
if (shouldRouteSpawnToWrfc(args)) {
|
|
73
|
+
const normalized: AgentToolArgs = {
|
|
74
|
+
...args,
|
|
75
|
+
task: selectAuthoritativeTask(args.task, lastUserMessage),
|
|
76
|
+
reviewMode: 'wrfc',
|
|
77
|
+
dangerously_disable_wrfc: false,
|
|
78
|
+
};
|
|
79
|
+
return cleanText(args.template) ? normalized : { ...normalized, template: 'engineer' };
|
|
80
|
+
}
|
|
68
81
|
return { ...args, reviewMode: 'none', dangerously_disable_wrfc: true };
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
if (args.mode !== 'batch-spawn') return args;
|
|
72
85
|
const tasks = Array.isArray(args.tasks) ? args.tasks.filter(isRecord) : [];
|
|
86
|
+
if (shouldCollapseBatchToAuthoritativeWrfc(args, tasks) && lastUserMessage) {
|
|
87
|
+
return buildAuthoritativeWrfcSpawn(args, tasks, lastUserMessage);
|
|
88
|
+
}
|
|
73
89
|
const wrfcTasks = tasks.filter((task) => isExplicitWrfcTask(task, args));
|
|
74
90
|
if (wrfcTasks.length === 0) {
|
|
75
91
|
return {
|
|
@@ -81,86 +97,122 @@ export function normalizeWrfcAgentToolInvocation(args: AgentToolArgs): AgentTool
|
|
|
81
97
|
: { ...task, reviewMode: 'none', dangerously_disable_wrfc: true }),
|
|
82
98
|
};
|
|
83
99
|
}
|
|
84
|
-
if (
|
|
100
|
+
if (wrfcTasks.length > 0) {
|
|
85
101
|
return {
|
|
86
102
|
...args,
|
|
87
103
|
reviewMode: 'wrfc',
|
|
88
104
|
dangerously_disable_wrfc: false,
|
|
89
|
-
tasks:
|
|
105
|
+
tasks: tasks.map((task) => isExplicitWrfcTask(task, args)
|
|
106
|
+
? { ...task, reviewMode: 'wrfc', dangerously_disable_wrfc: false }
|
|
107
|
+
: task),
|
|
90
108
|
};
|
|
91
109
|
}
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isRecord(value: unknown): value is AgentTaskArgs {
|
|
114
|
+
return Boolean(value) && typeof value === 'object';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function shouldRouteSpawnToWrfc(args: AgentToolArgs): boolean {
|
|
118
|
+
return isExplicitWrfcTask(args, args)
|
|
119
|
+
|| isRootReviewRoleTask(args)
|
|
120
|
+
|| isImplementationLikeTask(args);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function shouldCollapseBatchToAuthoritativeWrfc(root: AgentToolArgs, tasks: AgentTaskArgs[]): boolean {
|
|
124
|
+
if (tasks.length === 0) return false;
|
|
125
|
+
if (containsWrfcSignal(root.task)) return true;
|
|
126
|
+
return tasks.some((task) => isExplicitWrfcTask(task, root) || isRootReviewRoleTask(task));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function buildAuthoritativeWrfcSpawn(root: AgentToolArgs, tasks: AgentTaskArgs[], lastUserMessage: string): AgentToolArgs {
|
|
130
|
+
const context = [
|
|
131
|
+
cleanText(root.context) ? `Caller context:\n${cleanText(root.context)}` : null,
|
|
132
|
+
'TUI WRFC scope guard: the root model attempted to decompose one deliverable into role/root child tasks.',
|
|
133
|
+
'The authoritative task is the user request below. Proposed child tasks are non-authoritative context only.',
|
|
134
|
+
'Ignore any proposed child instruction that narrows the task to design-only/read-only/no-write unless the user request itself says that.',
|
|
135
|
+
`Authoritative user request:\n${lastUserMessage}`,
|
|
136
|
+
'Proposed child tasks:',
|
|
137
|
+
...tasks.map((task, index) => `${index + 1}. [${cleanText(task.template) || 'general'}] ${cleanText(task.task)}`),
|
|
138
|
+
].filter((line): line is string => Boolean(line)).join('\n');
|
|
92
139
|
|
|
93
|
-
const ownerTask = buildCollapsedWrfcOwnerTask(args, tasks);
|
|
94
140
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
141
|
+
mode: 'spawn',
|
|
142
|
+
task: lastUserMessage,
|
|
143
|
+
template: 'engineer',
|
|
144
|
+
model: root.model,
|
|
145
|
+
provider: root.provider,
|
|
146
|
+
fallbackModels: root.fallbackModels,
|
|
147
|
+
context,
|
|
148
|
+
successCriteria: uniqueStrings([
|
|
149
|
+
root.successCriteria,
|
|
150
|
+
...tasks.map((task) => task.successCriteria),
|
|
151
|
+
[
|
|
152
|
+
'Satisfy the original user request, not a narrowed child-task restatement.',
|
|
153
|
+
'Keep review, test, verification, and fix work inside this single WRFC owner chain.',
|
|
154
|
+
],
|
|
155
|
+
]),
|
|
156
|
+
requiredEvidence: uniqueStrings([
|
|
157
|
+
root.requiredEvidence,
|
|
158
|
+
...tasks.map((task) => task.requiredEvidence),
|
|
159
|
+
]),
|
|
160
|
+
writeScope: uniqueStrings([
|
|
161
|
+
root.writeScope,
|
|
162
|
+
...tasks.map((task) => task.writeScope),
|
|
163
|
+
]),
|
|
164
|
+
executionProtocol: root.executionProtocol ?? 'gather-plan-apply',
|
|
97
165
|
reviewMode: 'wrfc',
|
|
98
166
|
dangerously_disable_wrfc: false,
|
|
99
|
-
|
|
167
|
+
cohort: root.cohort,
|
|
100
168
|
};
|
|
101
169
|
}
|
|
102
170
|
|
|
103
|
-
function isRecord(value: unknown): value is AgentTaskArgs {
|
|
104
|
-
return Boolean(value) && typeof value === 'object';
|
|
105
|
-
}
|
|
106
|
-
|
|
107
171
|
function isExplicitWrfcTask(task: AgentTaskArgs, root: AgentToolArgs): boolean {
|
|
108
172
|
const disabled = task.dangerously_disable_wrfc === true || root.dangerously_disable_wrfc === true;
|
|
109
|
-
if (disabled) return false;
|
|
173
|
+
if (disabled && !containsWrfcSignal(task.task) && !containsWrfcSignal(root.task)) return false;
|
|
110
174
|
return task.reviewMode === 'wrfc'
|
|
111
175
|
|| root.reviewMode === 'wrfc'
|
|
112
176
|
|| containsWrfcSignal(task.task)
|
|
113
177
|
|| containsWrfcSignal(root.task);
|
|
114
178
|
}
|
|
115
179
|
|
|
116
|
-
function isBlockedOwnerTemplate(value: unknown): boolean {
|
|
117
|
-
if (typeof value !== 'string') return false;
|
|
118
|
-
return OWNER_BLOCKED_TEMPLATES.has(value.trim().toLowerCase());
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function isBlockedRootTask(task: AgentTaskArgs, root: AgentToolArgs): boolean {
|
|
122
|
-
if (isBlockedOwnerTemplate(task.template ?? root.template)) return true;
|
|
123
|
-
if (typeof task.task !== 'string') return false;
|
|
124
|
-
const normalized = task.task.trim().toLowerCase();
|
|
125
|
-
return OWNER_BLOCKED_TASK_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function normalizeOwnerTemplate(value: unknown): string {
|
|
129
|
-
if (isBlockedOwnerTemplate(value)) return 'engineer';
|
|
130
|
-
return typeof value === 'string' && value.trim().length > 0 ? value : 'engineer';
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function buildCollapsedWrfcOwnerTask(root: AgentToolArgs, taskList: AgentTaskArgs[]): AgentTaskArgs {
|
|
134
|
-
const firstOwner = taskList.find((task) => isExplicitWrfcTask(task, root) && !isBlockedRootTask(task, root))
|
|
135
|
-
?? taskList.find((task) => !isBlockedRootTask(task, root))
|
|
136
|
-
?? taskList[0]
|
|
137
|
-
?? {};
|
|
138
|
-
const taskLines = taskList.map((task, index) => {
|
|
139
|
-
const body = typeof task.task === 'string' && task.task.trim().length > 0 ? task.task.trim() : '(missing task text)';
|
|
140
|
-
const template = typeof task.template === 'string' && task.template.trim().length > 0 ? task.template.trim() : 'default';
|
|
141
|
-
return `${index + 1}. [${template}] ${body}`;
|
|
142
|
-
});
|
|
143
|
-
const rootTask = typeof root.task === 'string' && root.task.trim().length > 0
|
|
144
|
-
? `\n\nRoot task:\n${root.task.trim()}`
|
|
145
|
-
: '';
|
|
146
|
-
return {
|
|
147
|
-
...firstOwner,
|
|
148
|
-
task: [
|
|
149
|
-
'Complete the requested work as a single WRFC owner chain.',
|
|
150
|
-
'Do not spawn reviewer, verifier, tester, or parallel root WRFC agents yourself.',
|
|
151
|
-
'Use the attempted batch items below as context for one coherent owner deliverable; the WRFC controller will create review/fix agents after owner output exists.',
|
|
152
|
-
rootTask,
|
|
153
|
-
'',
|
|
154
|
-
'Attempted batch items:',
|
|
155
|
-
...taskLines,
|
|
156
|
-
].join('\n'),
|
|
157
|
-
template: normalizeOwnerTemplate(firstOwner.template ?? root.template),
|
|
158
|
-
reviewMode: 'wrfc',
|
|
159
|
-
dangerously_disable_wrfc: false,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
180
|
function containsWrfcSignal(value: unknown): boolean {
|
|
164
181
|
if (typeof value !== 'string') return false;
|
|
165
182
|
return /\bwrfc\b|work[-\s]*review[-\s]*fix/i.test(value);
|
|
166
183
|
}
|
|
184
|
+
|
|
185
|
+
function isRootReviewRoleTask(task: AgentTaskArgs): boolean {
|
|
186
|
+
const template = cleanText(task.template).toLowerCase();
|
|
187
|
+
if (/^(reviewer|tester|verifier|review|test|qa)$/.test(template)) return true;
|
|
188
|
+
const text = cleanText(task.task);
|
|
189
|
+
return /^\s*(?:\[?\s*)?(?:reviewer|tester|verifier|qa|quality\s+assurance|test|review|verify|validator)\b[\]\s:;-]*/i.test(text)
|
|
190
|
+
|| /\b(?:test|tests|testing|review|reviews|reviewing|verify|verifies|verifying|verification|validate|validates|validating|validation|qa)\s+(?:the|this|that|implementation|solution|feature|deliverable|code|changes|work|output|result|patch|diff)\b/i.test(text);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function isImplementationLikeTask(task: AgentTaskArgs): boolean {
|
|
194
|
+
const text = cleanText(task.task);
|
|
195
|
+
if (!text) return false;
|
|
196
|
+
return /\b(?:build|implement|create|add|write|fix|repair|update|refactor|change|modify|deliver|make|patch)\b/i.test(text)
|
|
197
|
+
&& !isReadOnlyTask(text);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function isReadOnlyTask(text: string): boolean {
|
|
201
|
+
if (/\bdo\s+not\s+(?:write|edit|modify|change|create)\b|\bread[-\s]*only\b|\bwithout\s+(?:writing|editing|modifying|changing|creating)\b/i.test(text)) return true;
|
|
202
|
+
return /^\s*(?:inspect|research|read|find|list|summarize|analy[sz]e|explain)\b/i.test(text)
|
|
203
|
+
&& !/\b(?:build|implement|create|add|write|fix|repair|update|refactor|change|modify|deliver|make|patch)\b/i.test(text);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function selectAuthoritativeTask(candidate: unknown, lastUserMessage: string): string {
|
|
207
|
+
return lastUserMessage || cleanText(candidate);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function uniqueStrings(groups: readonly unknown[]): string[] | undefined {
|
|
211
|
+
const values = groups.flatMap((group) => Array.isArray(group) ? group : []).filter((value): value is string => typeof value === 'string' && value.trim().length > 0);
|
|
212
|
+
const unique = [...new Set(values)];
|
|
213
|
+
return unique.length > 0 ? unique : undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function cleanText(value: unknown): string {
|
|
217
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
218
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.19.
|
|
9
|
+
let _version = '0.19.88';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
_version = pkg.version ?? _version;
|