@burtson-labs/agent-core 1.6.13
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/LICENSE +201 -0
- package/README.md +88 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/activation.d.ts +60 -0
- package/dist/mcp/activation.d.ts.map +1 -0
- package/dist/mcp/activation.js +139 -0
- package/dist/mcp/activation.js.map +1 -0
- package/dist/mcp/clientPool.d.ts +202 -0
- package/dist/mcp/clientPool.d.ts.map +1 -0
- package/dist/mcp/clientPool.js +469 -0
- package/dist/mcp/clientPool.js.map +1 -0
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +28 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +43 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +130 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/toolAdapter.d.ts +57 -0
- package/dist/mcp/toolAdapter.d.ts.map +1 -0
- package/dist/mcp/toolAdapter.js +223 -0
- package/dist/mcp/toolAdapter.js.map +1 -0
- package/dist/mcp/types.d.ts +122 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +15 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/providers/deterministic-provider.d.ts +21 -0
- package/dist/providers/deterministic-provider.d.ts.map +1 -0
- package/dist/providers/deterministic-provider.js +80 -0
- package/dist/providers/deterministic-provider.js.map +1 -0
- package/dist/providers/provider-client.d.ts +12 -0
- package/dist/providers/provider-client.d.ts.map +1 -0
- package/dist/providers/provider-client.js +11 -0
- package/dist/providers/provider-client.js.map +1 -0
- package/dist/runtime/AgentRuntime.d.ts +67 -0
- package/dist/runtime/AgentRuntime.d.ts.map +1 -0
- package/dist/runtime/AgentRuntime.js +382 -0
- package/dist/runtime/AgentRuntime.js.map +1 -0
- package/dist/security/secretPatterns.d.ts +76 -0
- package/dist/security/secretPatterns.d.ts.map +1 -0
- package/dist/security/secretPatterns.js +290 -0
- package/dist/security/secretPatterns.js.map +1 -0
- package/dist/tools/ask-user-tool.d.ts +19 -0
- package/dist/tools/ask-user-tool.d.ts.map +1 -0
- package/dist/tools/ask-user-tool.js +148 -0
- package/dist/tools/ask-user-tool.js.map +1 -0
- package/dist/tools/compactMessages.d.ts +52 -0
- package/dist/tools/compactMessages.d.ts.map +1 -0
- package/dist/tools/compactMessages.js +158 -0
- package/dist/tools/compactMessages.js.map +1 -0
- package/dist/tools/core-tools.d.ts +29 -0
- package/dist/tools/core-tools.d.ts.map +1 -0
- package/dist/tools/core-tools.js +2214 -0
- package/dist/tools/core-tools.js.map +1 -0
- package/dist/tools/git-tools.d.ts +32 -0
- package/dist/tools/git-tools.d.ts.map +1 -0
- package/dist/tools/git-tools.js +330 -0
- package/dist/tools/git-tools.js.map +1 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +31 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/language-adapters.d.ts +48 -0
- package/dist/tools/language-adapters.d.ts.map +1 -0
- package/dist/tools/language-adapters.js +299 -0
- package/dist/tools/language-adapters.js.map +1 -0
- package/dist/tools/loop/compactionTrigger.d.ts +47 -0
- package/dist/tools/loop/compactionTrigger.d.ts.map +1 -0
- package/dist/tools/loop/compactionTrigger.js +32 -0
- package/dist/tools/loop/compactionTrigger.js.map +1 -0
- package/dist/tools/loop/finalAnswerNudges.d.ts +68 -0
- package/dist/tools/loop/finalAnswerNudges.d.ts.map +1 -0
- package/dist/tools/loop/finalAnswerNudges.js +87 -0
- package/dist/tools/loop/finalAnswerNudges.js.map +1 -0
- package/dist/tools/loop/goalAnchor.d.ts +72 -0
- package/dist/tools/loop/goalAnchor.d.ts.map +1 -0
- package/dist/tools/loop/goalAnchor.js +76 -0
- package/dist/tools/loop/goalAnchor.js.map +1 -0
- package/dist/tools/loop/llmStream.d.ts +70 -0
- package/dist/tools/loop/llmStream.d.ts.map +1 -0
- package/dist/tools/loop/llmStream.js +181 -0
- package/dist/tools/loop/llmStream.js.map +1 -0
- package/dist/tools/loop/parallelExecute.d.ts +57 -0
- package/dist/tools/loop/parallelExecute.d.ts.map +1 -0
- package/dist/tools/loop/parallelExecute.js +54 -0
- package/dist/tools/loop/parallelExecute.js.map +1 -0
- package/dist/tools/loop/singleToolExecute.d.ts +71 -0
- package/dist/tools/loop/singleToolExecute.d.ts.map +1 -0
- package/dist/tools/loop/singleToolExecute.js +139 -0
- package/dist/tools/loop/singleToolExecute.js.map +1 -0
- package/dist/tools/loop/toolCallNormalize.d.ts +57 -0
- package/dist/tools/loop/toolCallNormalize.d.ts.map +1 -0
- package/dist/tools/loop/toolCallNormalize.js +99 -0
- package/dist/tools/loop/toolCallNormalize.js.map +1 -0
- package/dist/tools/loop/turnSetup.d.ts +43 -0
- package/dist/tools/loop/turnSetup.d.ts.map +1 -0
- package/dist/tools/loop/turnSetup.js +48 -0
- package/dist/tools/loop/turnSetup.js.map +1 -0
- package/dist/tools/ocr.d.ts +52 -0
- package/dist/tools/ocr.d.ts.map +1 -0
- package/dist/tools/ocr.js +238 -0
- package/dist/tools/ocr.js.map +1 -0
- package/dist/tools/post-edit-checks.d.ts +46 -0
- package/dist/tools/post-edit-checks.d.ts.map +1 -0
- package/dist/tools/post-edit-checks.js +236 -0
- package/dist/tools/post-edit-checks.js.map +1 -0
- package/dist/tools/skill-loader.d.ts +94 -0
- package/dist/tools/skill-loader.d.ts.map +1 -0
- package/dist/tools/skill-loader.js +422 -0
- package/dist/tools/skill-loader.js.map +1 -0
- package/dist/tools/skill-registry.d.ts +44 -0
- package/dist/tools/skill-registry.d.ts.map +1 -0
- package/dist/tools/skill-registry.js +118 -0
- package/dist/tools/skill-registry.js.map +1 -0
- package/dist/tools/skill-types.d.ts +38 -0
- package/dist/tools/skill-types.d.ts.map +1 -0
- package/dist/tools/skill-types.js +10 -0
- package/dist/tools/skill-types.js.map +1 -0
- package/dist/tools/skills/code-review-skill.d.ts +9 -0
- package/dist/tools/skills/code-review-skill.d.ts.map +1 -0
- package/dist/tools/skills/code-review-skill.js +66 -0
- package/dist/tools/skills/code-review-skill.js.map +1 -0
- package/dist/tools/skills/core-skill.d.ts +13 -0
- package/dist/tools/skills/core-skill.d.ts.map +1 -0
- package/dist/tools/skills/core-skill.js +23 -0
- package/dist/tools/skills/core-skill.js.map +1 -0
- package/dist/tools/skills/git-skill.d.ts +10 -0
- package/dist/tools/skills/git-skill.d.ts.map +1 -0
- package/dist/tools/skills/git-skill.js +30 -0
- package/dist/tools/skills/git-skill.js.map +1 -0
- package/dist/tools/skills/index.d.ts +17 -0
- package/dist/tools/skills/index.d.ts.map +1 -0
- package/dist/tools/skills/index.js +49 -0
- package/dist/tools/skills/index.js.map +1 -0
- package/dist/tools/skills/interaction-skill.d.ts +14 -0
- package/dist/tools/skills/interaction-skill.d.ts.map +1 -0
- package/dist/tools/skills/interaction-skill.js +24 -0
- package/dist/tools/skills/interaction-skill.js.map +1 -0
- package/dist/tools/skills/mail-search-skill.d.ts +25 -0
- package/dist/tools/skills/mail-search-skill.d.ts.map +1 -0
- package/dist/tools/skills/mail-search-skill.js +343 -0
- package/dist/tools/skills/mail-search-skill.js.map +1 -0
- package/dist/tools/skills/plan-skill.d.ts +10 -0
- package/dist/tools/skills/plan-skill.d.ts.map +1 -0
- package/dist/tools/skills/plan-skill.js +126 -0
- package/dist/tools/skills/plan-skill.js.map +1 -0
- package/dist/tools/skills/semantic-search-skill.d.ts +22 -0
- package/dist/tools/skills/semantic-search-skill.d.ts.map +1 -0
- package/dist/tools/skills/semantic-search-skill.js +244 -0
- package/dist/tools/skills/semantic-search-skill.js.map +1 -0
- package/dist/tools/skills/test-gen-skill.d.ts +9 -0
- package/dist/tools/skills/test-gen-skill.d.ts.map +1 -0
- package/dist/tools/skills/test-gen-skill.js +123 -0
- package/dist/tools/skills/test-gen-skill.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +60 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +200 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/tool-types.d.ts +281 -0
- package/dist/tools/tool-types.d.ts.map +1 -0
- package/dist/tools/tool-types.js +10 -0
- package/dist/tools/tool-types.js.map +1 -0
- package/dist/tools/tool-use-loop.d.ts +231 -0
- package/dist/tools/tool-use-loop.d.ts.map +1 -0
- package/dist/tools/tool-use-loop.js +2057 -0
- package/dist/tools/tool-use-loop.js.map +1 -0
- package/dist/tools/tool-use-parser.d.ts +78 -0
- package/dist/tools/tool-use-parser.d.ts.map +1 -0
- package/dist/tools/tool-use-parser.js +427 -0
- package/dist/tools/tool-use-parser.js.map +1 -0
- package/dist/tools/toolAvailabilityDetector.d.ts +48 -0
- package/dist/tools/toolAvailabilityDetector.d.ts.map +1 -0
- package/dist/tools/toolAvailabilityDetector.js +156 -0
- package/dist/tools/toolAvailabilityDetector.js.map +1 -0
- package/dist/tools/unified-patch.d.ts +87 -0
- package/dist/tools/unified-patch.d.ts.map +1 -0
- package/dist/tools/unified-patch.js +217 -0
- package/dist/tools/unified-patch.js.map +1 -0
- package/dist/types/agent.d.ts +69 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +54 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/tasks.d.ts +22 -0
- package/dist/types/tasks.d.ts.map +1 -0
- package/dist/types/tasks.js +3 -0
- package/dist/types/tasks.js.map +1 -0
- package/dist/utils/event-emitter.d.ts +13 -0
- package/dist/utils/event-emitter.d.ts.map +1 -0
- package/dist/utils/event-emitter.js +54 -0
- package/dist/utils/event-emitter.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language-aware pre-write validation adapters.
|
|
3
|
+
*
|
|
4
|
+
* Each adapter checks file content for syntax errors *before* write_file
|
|
5
|
+
* commits the content to disk. On failure the tool returns an isError result
|
|
6
|
+
* so the model can self-correct without ever writing an invalid file.
|
|
7
|
+
*
|
|
8
|
+
* All subprocess-based adapters (Python, TypeScript, C#) silently pass through
|
|
9
|
+
* (ok: true) when the required runtime / compiler is not found on PATH, so the
|
|
10
|
+
* agent degrades gracefully on systems without those tools installed.
|
|
11
|
+
*
|
|
12
|
+
* Adapter routing is by file extension (without leading dot, e.g. "ts").
|
|
13
|
+
*/
|
|
14
|
+
import type { ToolExecutionContext, ILanguageAdapterRegistry, ValidationResult } from './tool-types';
|
|
15
|
+
export interface LanguageAdapter {
|
|
16
|
+
/** File extensions this adapter handles, without leading dot (e.g. ['ts', 'tsx']). */
|
|
17
|
+
extensions: string[];
|
|
18
|
+
validate(filePath: string, content: string, ctx: ToolExecutionContext): Promise<ValidationResult>;
|
|
19
|
+
}
|
|
20
|
+
export declare class LanguageAdapterRegistry implements ILanguageAdapterRegistry {
|
|
21
|
+
private readonly adapters;
|
|
22
|
+
register(adapter: LanguageAdapter): this;
|
|
23
|
+
validate(filePath: string, content: string, ctx: ToolExecutionContext): Promise<ValidationResult>;
|
|
24
|
+
}
|
|
25
|
+
export declare class JsonAdapter implements LanguageAdapter {
|
|
26
|
+
readonly extensions: string[];
|
|
27
|
+
validate(_filePath: string, content: string, _ctx: ToolExecutionContext): Promise<ValidationResult>;
|
|
28
|
+
}
|
|
29
|
+
export declare class PythonAdapter implements LanguageAdapter {
|
|
30
|
+
readonly extensions: string[];
|
|
31
|
+
validate(_filePath: string, content: string, ctx: ToolExecutionContext): Promise<ValidationResult>;
|
|
32
|
+
}
|
|
33
|
+
export declare class TypeScriptAdapter implements LanguageAdapter {
|
|
34
|
+
readonly extensions: string[];
|
|
35
|
+
validate(_filePath: string, content: string, ctx: ToolExecutionContext): Promise<ValidationResult>;
|
|
36
|
+
}
|
|
37
|
+
export declare class CSharpAdapter implements LanguageAdapter {
|
|
38
|
+
readonly extensions: string[];
|
|
39
|
+
validate(_filePath: string, content: string, ctx: ToolExecutionContext): Promise<ValidationResult>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a registry pre-loaded with all built-in language adapters:
|
|
43
|
+
* JSON, Python, TypeScript/TSX, and C#.
|
|
44
|
+
*
|
|
45
|
+
* Each adapter silently passes through when its runtime/compiler is unavailable.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createDefaultLanguageAdapters(): LanguageAdapterRegistry;
|
|
48
|
+
//# sourceMappingURL=language-adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-adapters.d.ts","sourceRoot":"","sources":["../../src/tools/language-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIrG,MAAM,WAAW,eAAe;IAC9B,sFAAsF;IACtF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACnG;AAID,qBAAa,uBAAwB,YAAW,wBAAwB;IACtE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAElD,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAKlC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAQxG;AAID,qBAAa,WAAY,YAAW,eAAe;IACjD,QAAQ,CAAC,UAAU,WAAY;IAEzB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAQ1G;AAID,qBAAa,aAAc,YAAW,eAAe;IACnD,QAAQ,CAAC,UAAU,WAAU;IAEvB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAczG;AAID,qBAAa,iBAAkB,YAAW,eAAe;IACvD,QAAQ,CAAC,UAAU,WAAiB;IAE9B,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA2BzG;AAsFD,qBAAa,aAAc,YAAW,eAAe;IACnD,QAAQ,CAAC,UAAU,WAAU;IAEvB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAoCzG;AAID;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,uBAAuB,CAMvE"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Language-aware pre-write validation adapters.
|
|
4
|
+
*
|
|
5
|
+
* Each adapter checks file content for syntax errors *before* write_file
|
|
6
|
+
* commits the content to disk. On failure the tool returns an isError result
|
|
7
|
+
* so the model can self-correct without ever writing an invalid file.
|
|
8
|
+
*
|
|
9
|
+
* All subprocess-based adapters (Python, TypeScript, C#) silently pass through
|
|
10
|
+
* (ok: true) when the required runtime / compiler is not found on PATH, so the
|
|
11
|
+
* agent degrades gracefully on systems without those tools installed.
|
|
12
|
+
*
|
|
13
|
+
* Adapter routing is by file extension (without leading dot, e.g. "ts").
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.CSharpAdapter = exports.TypeScriptAdapter = exports.PythonAdapter = exports.JsonAdapter = exports.LanguageAdapterRegistry = void 0;
|
|
50
|
+
exports.createDefaultLanguageAdapters = createDefaultLanguageAdapters;
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
53
|
+
class LanguageAdapterRegistry {
|
|
54
|
+
constructor() {
|
|
55
|
+
this.adapters = [];
|
|
56
|
+
}
|
|
57
|
+
register(adapter) {
|
|
58
|
+
this.adapters.push(adapter);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
async validate(filePath, content, ctx) {
|
|
62
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
63
|
+
const adapter = this.adapters.find(a => a.extensions.includes(ext));
|
|
64
|
+
if (!adapter) {
|
|
65
|
+
return { ok: true };
|
|
66
|
+
}
|
|
67
|
+
return adapter.validate(filePath, content, ctx);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.LanguageAdapterRegistry = LanguageAdapterRegistry;
|
|
71
|
+
// ── JSON adapter ──────────────────────────────────────────────────────────────
|
|
72
|
+
class JsonAdapter {
|
|
73
|
+
constructor() {
|
|
74
|
+
this.extensions = ['json'];
|
|
75
|
+
}
|
|
76
|
+
async validate(_filePath, content, _ctx) {
|
|
77
|
+
try {
|
|
78
|
+
JSON.parse(content);
|
|
79
|
+
return { ok: true };
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return { ok: false, error: `JSON syntax error: ${err.message}` };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.JsonAdapter = JsonAdapter;
|
|
87
|
+
// ── Python adapter ────────────────────────────────────────────────────────────
|
|
88
|
+
class PythonAdapter {
|
|
89
|
+
constructor() {
|
|
90
|
+
this.extensions = ['py'];
|
|
91
|
+
}
|
|
92
|
+
async validate(_filePath, content, ctx) {
|
|
93
|
+
// Base64-encode to avoid all shell-quoting issues (base64 chars: A-Za-z0-9+/=).
|
|
94
|
+
const b64 = Buffer.from(content).toString('base64');
|
|
95
|
+
const result = await ctx.runCommand('python3', ['-c', `import ast, base64; ast.parse(base64.b64decode("${b64}").decode())`]);
|
|
96
|
+
// exitCode 127 = command not found (python3 not on PATH) → pass through
|
|
97
|
+
if (result.exitCode === 0 || result.exitCode === 127) {
|
|
98
|
+
return { ok: true };
|
|
99
|
+
}
|
|
100
|
+
const detail = (result.stderr || result.stdout).trim();
|
|
101
|
+
return { ok: false, error: `Python syntax error:\n${detail}` };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.PythonAdapter = PythonAdapter;
|
|
105
|
+
// ── TypeScript / TSX adapter ──────────────────────────────────────────────────
|
|
106
|
+
class TypeScriptAdapter {
|
|
107
|
+
constructor() {
|
|
108
|
+
this.extensions = ['ts', 'tsx'];
|
|
109
|
+
}
|
|
110
|
+
async validate(_filePath, content, ctx) {
|
|
111
|
+
// Base64-encode content so it can be safely embedded in the node -e script.
|
|
112
|
+
const b64 = Buffer.from(content).toString('base64');
|
|
113
|
+
// The script tries to require 'typescript' from the workspace node_modules.
|
|
114
|
+
// If TypeScript is not installed, the catch block exits 0 (silent skip).
|
|
115
|
+
// ts.transpileModule() is the standard public API for isolated syntax checking —
|
|
116
|
+
// it reports parse errors without needing a full compiler project.
|
|
117
|
+
const script = [
|
|
118
|
+
'try{',
|
|
119
|
+
"const ts=require('typescript');",
|
|
120
|
+
`const r=ts.transpileModule(Buffer.from('${b64}','base64').toString(),`,
|
|
121
|
+
'{reportDiagnostics:true,compilerOptions:{strict:false,skipLibCheck:true}});',
|
|
122
|
+
'const d=r.diagnostics||[];',
|
|
123
|
+
'if(d.length){',
|
|
124
|
+
"process.stdout.write(d.map(x=>typeof x.messageText==='string'?x.messageText:x.messageText.messageText).join('\\n'));",
|
|
125
|
+
'process.exit(1);}',
|
|
126
|
+
'}catch(e){process.exit(0);}' // TypeScript package not found → skip
|
|
127
|
+
].join('');
|
|
128
|
+
const result = await ctx.runCommand('node', ['-e', script], ctx.workspaceRoot);
|
|
129
|
+
if (result.exitCode !== 0) {
|
|
130
|
+
const detail = (result.stdout || result.stderr).trim();
|
|
131
|
+
return { ok: false, error: `TypeScript syntax error:\n${detail}` };
|
|
132
|
+
}
|
|
133
|
+
return { ok: true };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.TypeScriptAdapter = TypeScriptAdapter;
|
|
137
|
+
// ── C# adapter ────────────────────────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* Cheap C# sanity check that runs even when no Mono/.NET SDK is installed.
|
|
140
|
+
* Catches the common `apply_edit` corruption mode on
|
|
141
|
+
* S3Api/FileController.cs — the final edit left orphaned method-body
|
|
142
|
+
* fragments *after* the class's closing brace, which csc catches as
|
|
143
|
+
* CS1022/CS1008 but silently passes when csc is absent (the adapter
|
|
144
|
+
* exits 0 on ENOENT as a deliberate skip).
|
|
145
|
+
*
|
|
146
|
+
* Strict bracket balancing only — no parser, no AST. String literals
|
|
147
|
+
* and `//` / `/* ... *\/` comments are skipped so braces inside them
|
|
148
|
+
* don't confuse the count. False positives on weird but legal code are
|
|
149
|
+
* possible but rare; the trade-off is cheap universal coverage vs zero
|
|
150
|
+
* coverage without a toolchain.
|
|
151
|
+
*/
|
|
152
|
+
function cSharpStructureCheck(content) {
|
|
153
|
+
let braces = 0;
|
|
154
|
+
let parens = 0;
|
|
155
|
+
let brackets = 0;
|
|
156
|
+
let i = 0;
|
|
157
|
+
const n = content.length;
|
|
158
|
+
while (i < n) {
|
|
159
|
+
const ch = content[i];
|
|
160
|
+
// Line comment — skip to newline.
|
|
161
|
+
if (ch === '/' && content[i + 1] === '/') {
|
|
162
|
+
while (i < n && content[i] !== '\n')
|
|
163
|
+
i++;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Block comment — skip to closing */.
|
|
167
|
+
if (ch === '/' && content[i + 1] === '*') {
|
|
168
|
+
i += 2;
|
|
169
|
+
while (i < n && !(content[i] === '*' && content[i + 1] === '/'))
|
|
170
|
+
i++;
|
|
171
|
+
i += 2;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
// String / char / verbatim-string / interpolated-string — skip to
|
|
175
|
+
// the matching delimiter. Interpolated strings can contain braces
|
|
176
|
+
// that are NOT structural, so we must not count them.
|
|
177
|
+
if (ch === '"' || ch === '\'' || (ch === '@' && content[i + 1] === '"') || (ch === '$' && content[i + 1] === '"')) {
|
|
178
|
+
const verbatim = ch === '@';
|
|
179
|
+
const interpolated = ch === '$';
|
|
180
|
+
const quote = verbatim || interpolated ? '"' : ch;
|
|
181
|
+
i += verbatim || interpolated ? 2 : 1;
|
|
182
|
+
let interpDepth = 0;
|
|
183
|
+
while (i < n) {
|
|
184
|
+
const c = content[i];
|
|
185
|
+
if (!verbatim && c === '\\' && content[i + 1] !== undefined) {
|
|
186
|
+
i += 2;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (interpolated && c === '{' && content[i + 1] !== '{') {
|
|
190
|
+
interpDepth++;
|
|
191
|
+
i++;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (interpolated && c === '}' && interpDepth > 0) {
|
|
195
|
+
interpDepth--;
|
|
196
|
+
i++;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (c === quote && interpDepth === 0) {
|
|
200
|
+
i++;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (ch === '{')
|
|
208
|
+
braces++;
|
|
209
|
+
else if (ch === '}')
|
|
210
|
+
braces--;
|
|
211
|
+
else if (ch === '(')
|
|
212
|
+
parens++;
|
|
213
|
+
else if (ch === ')')
|
|
214
|
+
parens--;
|
|
215
|
+
else if (ch === '[')
|
|
216
|
+
brackets++;
|
|
217
|
+
else if (ch === ']')
|
|
218
|
+
brackets--;
|
|
219
|
+
// An early negative count means a close-bracket with no matching
|
|
220
|
+
// open — definitive corruption, no need to scan the rest.
|
|
221
|
+
if (braces < 0 || parens < 0 || brackets < 0) {
|
|
222
|
+
return {
|
|
223
|
+
ok: false,
|
|
224
|
+
error: `Unbalanced ${braces < 0 ? 'curly brace' : parens < 0 ? 'parenthesis' : 'square bracket'} — a closing delimiter appears with no matching opener (position ${i}). This usually means the last apply_edit truncated or duplicated code at a boundary. Re-read the file and reapply the intended change against the current file content.`
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
i++;
|
|
228
|
+
}
|
|
229
|
+
if (braces !== 0 || parens !== 0 || brackets !== 0) {
|
|
230
|
+
const detail = [];
|
|
231
|
+
if (braces !== 0)
|
|
232
|
+
detail.push(`${braces > 0 ? braces + ' unclosed' : -braces + ' extra'} curly brace${Math.abs(braces) === 1 ? '' : 's'}`);
|
|
233
|
+
if (parens !== 0)
|
|
234
|
+
detail.push(`${parens > 0 ? parens + ' unclosed' : -parens + ' extra'} parenthes${Math.abs(parens) === 1 ? 'is' : 'es'}`);
|
|
235
|
+
if (brackets !== 0)
|
|
236
|
+
detail.push(`${brackets > 0 ? brackets + ' unclosed' : -brackets + ' extra'} square bracket${Math.abs(brackets) === 1 ? '' : 's'}`);
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
error: `C# structure check: ${detail.join(', ')}. The file is not syntactically balanced. Re-read the file and reapply the intended change against the current content.`
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { ok: true };
|
|
243
|
+
}
|
|
244
|
+
class CSharpAdapter {
|
|
245
|
+
constructor() {
|
|
246
|
+
this.extensions = ['cs'];
|
|
247
|
+
}
|
|
248
|
+
async validate(_filePath, content, ctx) {
|
|
249
|
+
// First: cheap structural check that always runs. Catches the
|
|
250
|
+
// truncated-close-brace / duplicated-fragment corruption mode from
|
|
251
|
+
// broken apply_edit calls even when no compiler is installed.
|
|
252
|
+
const structural = cSharpStructureCheck(content);
|
|
253
|
+
if (!structural.ok)
|
|
254
|
+
return structural;
|
|
255
|
+
// Base64-encode content for safe interpolation into the node -e script.
|
|
256
|
+
const b64 = Buffer.from(content).toString('base64');
|
|
257
|
+
// The script:
|
|
258
|
+
// 1. Writes content to an OS temp file
|
|
259
|
+
// 2. Tries 'csc' (Mono C# compiler), then 'mcs' (older Mono alias)
|
|
260
|
+
// 3. Cleans up the temp file + output DLL regardless of outcome
|
|
261
|
+
// 4. If neither compiler is found (ENOENT), exits 0 → silent skip
|
|
262
|
+
const script = [
|
|
263
|
+
"const fs=require('fs'),os=require('os'),cp=require('child_process');",
|
|
264
|
+
"const tmp=os.tmpdir()+'/bandit_'+process.pid+'.cs';",
|
|
265
|
+
"const out=tmp+'.dll';",
|
|
266
|
+
'try{',
|
|
267
|
+
` fs.writeFileSync(tmp,Buffer.from('${b64}','base64'));`,
|
|
268
|
+
" let r=cp.spawnSync('csc',['/nologo','/t:library','/out:'+out,tmp],{encoding:'utf8'});",
|
|
269
|
+
" if(r.error&&r.error.code==='ENOENT')",
|
|
270
|
+
" r=cp.spawnSync('mcs',['-nologo','-t:library','-out:'+out,tmp],{encoding:'utf8'});",
|
|
271
|
+
' try{fs.unlinkSync(tmp);fs.unlinkSync(out);}catch{}',
|
|
272
|
+
" if(r.error&&r.error.code==='ENOENT')process.exit(0);",
|
|
273
|
+
" if(r.status!==0){process.stdout.write((r.stdout||'')+(r.stderr||''));process.exit(1);}",
|
|
274
|
+
"}catch(e){try{fs.unlinkSync(tmp);}catch{}process.exit(0);}"
|
|
275
|
+
].join('\n');
|
|
276
|
+
const result = await ctx.runCommand('node', ['-e', script]);
|
|
277
|
+
if (result.exitCode !== 0) {
|
|
278
|
+
const detail = (result.stdout || result.stderr).trim();
|
|
279
|
+
return { ok: false, error: `C# compilation error:\n${detail}` };
|
|
280
|
+
}
|
|
281
|
+
return { ok: true };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
exports.CSharpAdapter = CSharpAdapter;
|
|
285
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
286
|
+
/**
|
|
287
|
+
* Creates a registry pre-loaded with all built-in language adapters:
|
|
288
|
+
* JSON, Python, TypeScript/TSX, and C#.
|
|
289
|
+
*
|
|
290
|
+
* Each adapter silently passes through when its runtime/compiler is unavailable.
|
|
291
|
+
*/
|
|
292
|
+
function createDefaultLanguageAdapters() {
|
|
293
|
+
return new LanguageAdapterRegistry()
|
|
294
|
+
.register(new JsonAdapter())
|
|
295
|
+
.register(new PythonAdapter())
|
|
296
|
+
.register(new TypeScriptAdapter())
|
|
297
|
+
.register(new CSharpAdapter());
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=language-adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-adapters.js","sourceRoot":"","sources":["../../src/tools/language-adapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-iteration compaction trigger for ToolUseLoop.runWithMessages.
|
|
3
|
+
*
|
|
4
|
+
* Decides whether the running message log needs compaction this
|
|
5
|
+
* iteration, runs `compactToolMessages` if so, mutates the messages
|
|
6
|
+
* array in place, emits `tool_loop:compacted`, and signals whether
|
|
7
|
+
* the compaction was *aggressive* (large enough drop to warrant a
|
|
8
|
+
* goal-anchor re-injection on the same iteration).
|
|
9
|
+
*
|
|
10
|
+
* Aggressive thresholds (preserved from the in-class implementation):
|
|
11
|
+
*
|
|
12
|
+
* dropRatio >= 0.25 OR dropAbsolute >= 10,000 tokens
|
|
13
|
+
*
|
|
14
|
+
* Threshold history (kept for the why-trace):
|
|
15
|
+
* - 2026-05-06: original threshold was >50% drop or >20k absolute.
|
|
16
|
+
* Caught the 43k→4.5k case but missed lower-percentage drops.
|
|
17
|
+
* - 2026-05-07: lowered to >25% drop or >10k absolute after an
|
|
18
|
+
* 81k→75k compaction (only 7% drop, 6k absolute) was followed by
|
|
19
|
+
* the model fabricating a fake user prompt out of comments it had
|
|
20
|
+
* read in `AgentRuntime.ts`. Below-threshold compactions weren't
|
|
21
|
+
* firing the re-anchor and the model drifted unchallenged.
|
|
22
|
+
*
|
|
23
|
+
* The in-place mutation matters — subsequent iterations need to see
|
|
24
|
+
* the compacted history so earlier tool results don't grow back every
|
|
25
|
+
* time the loop re-enters.
|
|
26
|
+
*/
|
|
27
|
+
import type { ToolLoopMessage } from '../tool-types';
|
|
28
|
+
export type CompactionEmit = (type: string, payload?: unknown) => void;
|
|
29
|
+
export interface ApplyCompactionArgs {
|
|
30
|
+
/** Mutable message log — replaced in place when compaction fires. */
|
|
31
|
+
messages: ToolLoopMessage[];
|
|
32
|
+
/** Token budget for the chat messages. `undefined`/`<=0`/non-finite
|
|
33
|
+
* disables compaction (returns aggressive=false without doing work). */
|
|
34
|
+
tokenBudget: number | undefined;
|
|
35
|
+
/** Event sink for `tool_loop:compacted`. */
|
|
36
|
+
emit: CompactionEmit;
|
|
37
|
+
/** Current iteration counter — propagated into the event payload. */
|
|
38
|
+
iteration: number;
|
|
39
|
+
}
|
|
40
|
+
export interface ApplyCompactionResult {
|
|
41
|
+
/** True when this iteration's compaction dropped >=25% of tokens OR
|
|
42
|
+
* >=10,000 absolute tokens. The orchestrator uses this to override
|
|
43
|
+
* the eligibility floor and refire gap on the goal-anchor injection. */
|
|
44
|
+
aggressive: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function applyCompactionIfNeeded(args: ApplyCompactionArgs): ApplyCompactionResult;
|
|
47
|
+
//# sourceMappingURL=compactionTrigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compactionTrigger.d.ts","sourceRoot":"","sources":["../../../src/tools/loop/compactionTrigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B;4EACwE;IACxE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,4CAA4C;IAC5C,IAAI,EAAE,cAAc,CAAC;IACrB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC;;4EAEwE;IACxE,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,GAAG,qBAAqB,CAgCxF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyCompactionIfNeeded = applyCompactionIfNeeded;
|
|
4
|
+
const compactMessages_1 = require("../compactMessages");
|
|
5
|
+
function applyCompactionIfNeeded(args) {
|
|
6
|
+
const { messages, tokenBudget, emit, iteration } = args;
|
|
7
|
+
if (tokenBudget === undefined || tokenBudget <= 0 || !Number.isFinite(tokenBudget)) {
|
|
8
|
+
return { aggressive: false };
|
|
9
|
+
}
|
|
10
|
+
const report = (0, compactMessages_1.compactToolMessages)(messages, { tokenBudget });
|
|
11
|
+
if (report.messagesCompacted === 0) {
|
|
12
|
+
return { aggressive: false };
|
|
13
|
+
}
|
|
14
|
+
emit('tool_loop:compacted', {
|
|
15
|
+
iteration,
|
|
16
|
+
messagesCompacted: report.messagesCompacted,
|
|
17
|
+
beforeTokens: report.beforeTokens,
|
|
18
|
+
afterTokens: report.afterTokens
|
|
19
|
+
});
|
|
20
|
+
const dropRatio = report.beforeTokens > 0
|
|
21
|
+
? (report.beforeTokens - report.afterTokens) / report.beforeTokens
|
|
22
|
+
: 0;
|
|
23
|
+
const dropAbsolute = report.beforeTokens - report.afterTokens;
|
|
24
|
+
const aggressive = dropRatio >= 0.25 || dropAbsolute >= 10000;
|
|
25
|
+
// Replace messages in place so subsequent iterations keep the
|
|
26
|
+
// compacted history — if we didn't, earlier tool results would
|
|
27
|
+
// grow back every time the loop re-entered.
|
|
28
|
+
messages.length = 0;
|
|
29
|
+
messages.push(...report.compacted);
|
|
30
|
+
return { aggressive };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=compactionTrigger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compactionTrigger.js","sourceRoot":"","sources":["../../../src/tools/loop/compactionTrigger.ts"],"names":[],"mappings":";;AAkDA,0DAgCC;AAvDD,wDAAyD;AAuBzD,SAAgB,uBAAuB,CAAC,IAAyB;IAC/D,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAExD,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,qCAAmB,EAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,iBAAiB,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC,qBAAqB,EAAE;QAC1B,SAAS;QACT,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC;QACvC,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,YAAY;QAClE,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC;IAC9D,MAAM,UAAU,GAAG,SAAS,IAAI,IAAI,IAAI,YAAY,IAAI,KAAM,CAAC;IAE/D,8DAA8D;IAC9D,+DAA+D;IAC/D,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAEnC,OAAO,EAAE,UAAU,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Announce-intent + ask-user nudge detectors for the final-response
|
|
3
|
+
* branch of ToolUseLoop.runWithMessages.
|
|
4
|
+
*
|
|
5
|
+
* Both run AFTER the loop has decided the model's response has no tool
|
|
6
|
+
* calls (i.e. the response is on track to become the final answer) but
|
|
7
|
+
* BEFORE we commit to that as the turn's exit. They catch two failure
|
|
8
|
+
* modes where the model has effectively stalled but the surrounding
|
|
9
|
+
* detectors don't catch it:
|
|
10
|
+
*
|
|
11
|
+
* 1. announce-intent — model said "Let me X" / "I'll Y" / "I'm currently
|
|
12
|
+
* porting Z" but emitted no tool call. The user sees an
|
|
13
|
+
* announcement, never the work. One nudge per turn; if the model
|
|
14
|
+
* ignores, the loop terminates and the user sees the announcement
|
|
15
|
+
* as the final answer.
|
|
16
|
+
*
|
|
17
|
+
* The NARRATE_PROGRESS_RE gerund branch requires a syntactic
|
|
18
|
+
* complement (preposition / article / object pronoun) after the
|
|
19
|
+
* -ing verb so casual greeting prose ("I'm doing well", "I'm feeling
|
|
20
|
+
* fine", "I'm going home") doesn't false-positive. The pre-2026-06-02
|
|
21
|
+
* version of that branch was `i(?:'m| am)\s+\w+ing` alone, which
|
|
22
|
+
* matched "I'm doing" on every casual conversational reply and
|
|
23
|
+
* chewed through the no-tool-call hard cap. Regression captured in
|
|
24
|
+
* `intentNudgeDetectors.test.ts`.
|
|
25
|
+
*
|
|
26
|
+
* 2. ask-user — model asked the user a decision question ("Shall I
|
|
27
|
+
* proceed?", "Do you want me to continue?") in plain prose while
|
|
28
|
+
* the `ask_user` tool is registered. The interactive prompt is the
|
|
29
|
+
* surface the user expects; passive prose loses the round-trip.
|
|
30
|
+
* One nudge per turn.
|
|
31
|
+
*
|
|
32
|
+
* Both detectors:
|
|
33
|
+
* - read from `finalResponse` (the response after tool_call markup
|
|
34
|
+
* has been stripped)
|
|
35
|
+
* - strip reasoning fences before inspecting the visible prose
|
|
36
|
+
* - return `{ fired }` so the orchestrator can update its mutable
|
|
37
|
+
* `*Nudged` flag, push the returned message, and `continue` the loop
|
|
38
|
+
*
|
|
39
|
+
* The orchestrator is responsible for the once-per-turn guard
|
|
40
|
+
* (`!announceIntentNudged` / `!askUserNudged` predicates in the gate
|
|
41
|
+
* args) — the detectors only check the SHAPE of the response.
|
|
42
|
+
*/
|
|
43
|
+
import type { ToolLoopMessage } from '../tool-types';
|
|
44
|
+
export type NudgeEmit = (type: string, payload?: unknown) => void;
|
|
45
|
+
export interface AnnounceIntentNudgeArgs {
|
|
46
|
+
/** The response after tool_call markup has been stripped. The visible
|
|
47
|
+
* prose stripped of reasoning fences is computed internally. */
|
|
48
|
+
finalResponse: string;
|
|
49
|
+
/** Current iteration counter (propagated into the emit payload). */
|
|
50
|
+
iteration: number;
|
|
51
|
+
/** Event sink for `tool_loop:announce_intent_nudge`. */
|
|
52
|
+
emit: NudgeEmit;
|
|
53
|
+
}
|
|
54
|
+
export interface NudgeResult {
|
|
55
|
+
/** True when the detector matched. Caller pushes `message` and continues. */
|
|
56
|
+
fired: boolean;
|
|
57
|
+
/** The user message to append when fired. Undefined when not fired. */
|
|
58
|
+
message?: ToolLoopMessage;
|
|
59
|
+
}
|
|
60
|
+
export declare function tryAnnounceIntentNudge(args: AnnounceIntentNudgeArgs): NudgeResult;
|
|
61
|
+
export interface AskUserNudgeArgs extends AnnounceIntentNudgeArgs {
|
|
62
|
+
/** When false, ask-user tool isn't registered — the nudge is meaningless
|
|
63
|
+
* and never fires. The orchestrator can pre-gate, but the detector
|
|
64
|
+
* accepts the flag so the boundary stays narrow. */
|
|
65
|
+
askUserAvailable: boolean;
|
|
66
|
+
}
|
|
67
|
+
export declare function tryAskUserNudge(args: AskUserNudgeArgs): NudgeResult;
|
|
68
|
+
//# sourceMappingURL=finalAnswerNudges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finalAnswerNudges.d.ts","sourceRoot":"","sources":["../../../src/tools/loop/finalAnswerNudges.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACtC;oEACgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,KAAK,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AA2CD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,GAAG,WAAW,CAwBjF;AAED,MAAM,WAAW,gBAAiB,SAAQ,uBAAuB;IAC/D;;wDAEoD;IACpD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAUD,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,WAAW,CAmBnE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tryAnnounceIntentNudge = tryAnnounceIntentNudge;
|
|
4
|
+
exports.tryAskUserNudge = tryAskUserNudge;
|
|
5
|
+
const ANNOUNCE_RE = /^(?:next,?\s+|now,?\s+|then,?\s+)?(?:let me|let us|let'?s|i(?:'ll| will| am going to| 'm going to| 'm about to| should))\s+(?:dig|continue|explore|investigate|check|look|read|inspect|analyze|examine|review|verify|find|search|trace|walk|drill|keep|move on)/;
|
|
6
|
+
// gemma-family progress narration that ALSO exits without acting:
|
|
7
|
+
// "I am on it", "I'm currently porting…", "I've already started…",
|
|
8
|
+
// "I'll keep pushing…". The ^-anchored ANNOUNCE_RE above only catches
|
|
9
|
+
// "let me/I'll + investigation verb" and misses these present-
|
|
10
|
+
// progressive / on-it / start-claim openers.
|
|
11
|
+
//
|
|
12
|
+
// The gerund branch requires a complement (preposition/article/object
|
|
13
|
+
// pronoun) after the -ing verb so casual greeting prose doesn't
|
|
14
|
+
// false-positive. Pre-2026-06-02 the gerund branch was
|
|
15
|
+
// `i(?:'m| am)\s+\w+ing` alone, which matched "I'm doing well" (a
|
|
16
|
+
// perfectly fine reply to "how are you?") and chewed through the
|
|
17
|
+
// no-tool-call hard cap on every casual conversational turn. Real
|
|
18
|
+
// progress narration ("I'm porting the runtime", "I'm working on it",
|
|
19
|
+
// "I'm thinking through the problem") has a syntactic complement;
|
|
20
|
+
// "I'm doing well" / "I'm feeling fine" / "I'm going home" does not.
|
|
21
|
+
const NARRATE_PROGRESS_RE = new RegExp("^(?:next,?\\s+|now,?\\s+|then,?\\s+)?(?:" +
|
|
22
|
+
"i(?:'m| am)\\s+on it|" +
|
|
23
|
+
"i(?:'m| am)\\s+(?:currently\\s+|now\\s+)?\\w+ing\\s+(?:on|with|to|in|the|a|an|that|this|it|my|our|your|some|all|more|through|over|out)\\b|" +
|
|
24
|
+
"i(?:'ve| have)\\s+(?:already\\s+)?(?:started|begun|kicked off)|" +
|
|
25
|
+
"i(?:'ll| will)\\s+(?:keep|continue)\\b" +
|
|
26
|
+
")");
|
|
27
|
+
function stripReasoningFences(text) {
|
|
28
|
+
return text
|
|
29
|
+
.replace(/```bandit-reasoning\b[\s\S]*?```/gi, '')
|
|
30
|
+
.replace(/```bandit-reasoning\b[\s\S]*$/i, '')
|
|
31
|
+
.replace(/<think\b[\s\S]*?<\/think\s*>/gi, '')
|
|
32
|
+
.replace(/<think\b[\s\S]*$/i, '')
|
|
33
|
+
.trim();
|
|
34
|
+
}
|
|
35
|
+
const ANNOUNCE_INTENT_NUDGE_BODY = 'Your response announces an action ("Let me X", "I\'ll Y next") but emits NO tool call — so the loop will exit and the user only sees your announcement, not the result. ' +
|
|
36
|
+
'Take the action now: emit the `read_file`, `search_code`, `list_files`, `run_command`, or other tool call you just described. ' +
|
|
37
|
+
'If you have nothing more to investigate, write a complete final answer instead — do not announce future work without doing it.';
|
|
38
|
+
function tryAnnounceIntentNudge(args) {
|
|
39
|
+
const { finalResponse, iteration, emit } = args;
|
|
40
|
+
if (!finalResponse)
|
|
41
|
+
return { fired: false };
|
|
42
|
+
const visible = stripReasoningFences(finalResponse);
|
|
43
|
+
// Cap visible length so legitimate wrap-ups (a model writing a
|
|
44
|
+
// multi-paragraph summary that happens to start with "Let me
|
|
45
|
+
// explain …") aren't caught. The failure mode is a SHORT
|
|
46
|
+
// announcement followed by silence, not a long answer.
|
|
47
|
+
if (!visible || visible.length > 600)
|
|
48
|
+
return { fired: false };
|
|
49
|
+
const opener = visible.slice(0, 240).toLowerCase();
|
|
50
|
+
if (!ANNOUNCE_RE.test(opener) && !NARRATE_PROGRESS_RE.test(opener)) {
|
|
51
|
+
return { fired: false };
|
|
52
|
+
}
|
|
53
|
+
emit('tool_loop:announce_intent_nudge', {
|
|
54
|
+
iteration,
|
|
55
|
+
responsePreview: visible.slice(0, 240)
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
fired: true,
|
|
59
|
+
message: { role: 'user', content: ANNOUNCE_INTENT_NUDGE_BODY }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const DECISION_RE = /(shall i|should i|do you want me to|would you like me to|want me to (?:proceed|continue|go ahead)|which (?:option|approach|one)|proceed with the|go ahead with|or were you looking|or do you want|let me know (?:if|which|whether))/;
|
|
63
|
+
const ASK_USER_NUDGE_BODY = 'You asked the user to choose or approve, but you did it in prose and the loop is exiting — they get a passive question, not an interactive prompt. ' +
|
|
64
|
+
'Call the `ask_user` tool now: pose the question with 2–4 concrete options (put the recommended one first and append " (Recommended)"). ' +
|
|
65
|
+
'Do not ask for a decision in plain prose when ask_user is available.';
|
|
66
|
+
function tryAskUserNudge(args) {
|
|
67
|
+
const { finalResponse, iteration, emit, askUserAvailable } = args;
|
|
68
|
+
if (!askUserAvailable || !finalResponse)
|
|
69
|
+
return { fired: false };
|
|
70
|
+
const visibleQ = stripReasoningFences(finalResponse);
|
|
71
|
+
if (!visibleQ)
|
|
72
|
+
return { fired: false };
|
|
73
|
+
if (!/\?\s*$/.test(visibleQ))
|
|
74
|
+
return { fired: false };
|
|
75
|
+
const tail = visibleQ.slice(-320).toLowerCase();
|
|
76
|
+
if (!DECISION_RE.test(tail))
|
|
77
|
+
return { fired: false };
|
|
78
|
+
emit('tool_loop:ask_user_nudge', {
|
|
79
|
+
iteration,
|
|
80
|
+
responsePreview: visibleQ.slice(-240)
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
fired: true,
|
|
84
|
+
message: { role: 'user', content: ASK_USER_NUDGE_BODY }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=finalAnswerNudges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finalAnswerNudges.js","sourceRoot":"","sources":["../../../src/tools/loop/finalAnswerNudges.ts"],"names":[],"mappings":"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"}
|