@goondan/openharness-base 0.1.7 → 0.1.9
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/package.json +14 -9
- package/dist/extensions/compaction.d.ts +0 -12
- package/dist/extensions/compaction.d.ts.map +0 -1
- package/dist/extensions/compaction.js +0 -159
- package/dist/extensions/compaction.js.map +0 -1
- package/dist/extensions/context-message.d.ts +0 -9
- package/dist/extensions/context-message.d.ts.map +0 -1
- package/dist/extensions/context-message.js +0 -446
- package/dist/extensions/context-message.js.map +0 -1
- package/dist/extensions/index.d.ts +0 -13
- package/dist/extensions/index.d.ts.map +0 -1
- package/dist/extensions/index.js +0 -7
- package/dist/extensions/index.js.map +0 -1
- package/dist/extensions/logging.d.ts +0 -11
- package/dist/extensions/logging.d.ts.map +0 -1
- package/dist/extensions/logging.js +0 -140
- package/dist/extensions/logging.js.map +0 -1
- package/dist/extensions/message-window.d.ts +0 -7
- package/dist/extensions/message-window.d.ts.map +0 -1
- package/dist/extensions/message-window.js +0 -60
- package/dist/extensions/message-window.js.map +0 -1
- package/dist/extensions/required-tools-guard.d.ts +0 -9
- package/dist/extensions/required-tools-guard.d.ts.map +0 -1
- package/dist/extensions/required-tools-guard.js +0 -74
- package/dist/extensions/required-tools-guard.js.map +0 -1
- package/dist/extensions/tool-search.d.ts +0 -10
- package/dist/extensions/tool-search.d.ts.map +0 -1
- package/dist/extensions/tool-search.js +0 -198
- package/dist/extensions/tool-search.js.map +0 -1
- package/dist/harness.yaml +0 -503
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/manifests/base.d.ts +0 -8
- package/dist/manifests/base.d.ts.map +0 -1
- package/dist/manifests/base.js +0 -352
- package/dist/manifests/base.js.map +0 -1
- package/dist/manifests/index.d.ts +0 -3
- package/dist/manifests/index.d.ts.map +0 -1
- package/dist/manifests/index.js +0 -2
- package/dist/manifests/index.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -8
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js +0 -119
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/file-system.d.ts +0 -12
- package/dist/tools/file-system.d.ts.map +0 -1
- package/dist/tools/file-system.js +0 -117
- package/dist/tools/file-system.js.map +0 -1
- package/dist/tools/http-fetch.d.ts +0 -8
- package/dist/tools/http-fetch.d.ts.map +0 -1
- package/dist/tools/http-fetch.js +0 -149
- package/dist/tools/http-fetch.js.map +0 -1
- package/dist/tools/index.d.ts +0 -7
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -7
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/json-query.d.ts +0 -12
- package/dist/tools/json-query.d.ts.map +0 -1
- package/dist/tools/json-query.js +0 -176
- package/dist/tools/json-query.js.map +0 -1
- package/dist/tools/text-transform.d.ts +0 -16
- package/dist/tools/text-transform.d.ts.map +0 -1
- package/dist/tools/text-transform.js +0 -127
- package/dist/tools/text-transform.js.map +0 -1
- package/dist/tools/wait.d.ts +0 -6
- package/dist/tools/wait.d.ts.map +0 -1
- package/dist/tools/wait.js +0 -32
- package/dist/tools/wait.js.map +0 -1
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -17
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -159
- package/dist/utils.js.map +0 -1
- package/src/__tests__/basic-system-prompt.test.ts +0 -186
- package/src/__tests__/compaction-summarize.test.ts +0 -282
- package/src/__tests__/logging.test.ts +0 -200
- package/src/__tests__/message-window.test.ts +0 -194
- package/src/__tests__/required-tools-guard.test.ts +0 -207
- package/src/__tests__/tool-search.test.ts +0 -187
- package/src/__tests__/tools.test.ts +0 -332
- package/src/extensions/basic-system-prompt.ts +0 -48
- package/src/extensions/compaction-summarize.ts +0 -104
- package/src/extensions/logging.ts +0 -42
- package/src/extensions/message-window.ts +0 -23
- package/src/extensions/required-tools-guard.ts +0 -24
- package/src/extensions/tool-search.ts +0 -38
- package/src/index.ts +0 -16
- package/src/tools/bash.ts +0 -38
- package/src/tools/file-system.ts +0 -83
- package/src/tools/http-fetch.ts +0 -64
- package/src/tools/json-query.ts +0 -71
- package/src/tools/text-transform.ts +0 -59
- package/src/tools/wait.ts +0 -46
- package/tsconfig.json +0 -8
- package/vitest.config.ts +0 -7
package/dist/utils.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { JsonArray, JsonObject, JsonValue, Message } from './types.js';
|
|
2
|
-
export declare function createId(prefix: string): string;
|
|
3
|
-
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
4
|
-
export declare function isJsonValue(value: unknown): value is JsonValue;
|
|
5
|
-
export declare function isJsonObject(value: unknown): value is JsonObject;
|
|
6
|
-
export declare function isJsonArray(value: unknown): value is JsonArray;
|
|
7
|
-
export declare function parseJsonObject(text: string): JsonObject | null;
|
|
8
|
-
export declare function requireString(input: JsonObject, key: string): string;
|
|
9
|
-
export declare function optionalString(input: JsonObject, key: string): string | undefined;
|
|
10
|
-
export declare function optionalNumber(input: JsonObject, key: string, fallback?: number): number | undefined;
|
|
11
|
-
export declare function optionalBoolean(input: JsonObject, key: string, fallback?: boolean): boolean | undefined;
|
|
12
|
-
export declare function optionalStringArray(input: JsonObject, key: string): string[] | undefined;
|
|
13
|
-
export declare function optionalJsonObject(input: JsonObject, key: string): JsonObject | undefined;
|
|
14
|
-
export declare function resolveFromWorkdir(workdir: string, targetPath: string): string;
|
|
15
|
-
export declare function estimateMessageLength(message: Message): number;
|
|
16
|
-
export declare function truncateString(text: string, maxLength: number): string;
|
|
17
|
-
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAyB9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAYhE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAU/D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASjF;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,GAAG,SAAS,CASpB;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,GAAG,SAAS,CASrB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAiBxF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CASzF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAK9E;AAaD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAgB9D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQtE"}
|
package/dist/utils.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
export function createId(prefix) {
|
|
4
|
-
return `${prefix}_${randomUUID()}`;
|
|
5
|
-
}
|
|
6
|
-
export function isRecord(value) {
|
|
7
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8
|
-
}
|
|
9
|
-
export function isJsonValue(value) {
|
|
10
|
-
if (value === null) {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
const kind = typeof value;
|
|
14
|
-
if (kind === 'string' || kind === 'number' || kind === 'boolean') {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
if (Array.isArray(value)) {
|
|
18
|
-
return value.every((item) => isJsonValue(item));
|
|
19
|
-
}
|
|
20
|
-
if (!isRecord(value)) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
for (const candidate of Object.values(value)) {
|
|
24
|
-
if (!isJsonValue(candidate)) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
export function isJsonObject(value) {
|
|
31
|
-
if (!isRecord(value)) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
for (const candidate of Object.values(value)) {
|
|
35
|
-
if (!isJsonValue(candidate)) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
export function isJsonArray(value) {
|
|
42
|
-
return Array.isArray(value) && value.every((item) => isJsonValue(item));
|
|
43
|
-
}
|
|
44
|
-
export function parseJsonObject(text) {
|
|
45
|
-
try {
|
|
46
|
-
const parsed = JSON.parse(text);
|
|
47
|
-
if (isJsonObject(parsed)) {
|
|
48
|
-
return parsed;
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export function requireString(input, key) {
|
|
57
|
-
const value = input[key];
|
|
58
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
59
|
-
throw new Error(`'${key}' must be a non-empty string`);
|
|
60
|
-
}
|
|
61
|
-
return value;
|
|
62
|
-
}
|
|
63
|
-
export function optionalString(input, key) {
|
|
64
|
-
const value = input[key];
|
|
65
|
-
if (value === undefined) {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
if (typeof value !== 'string') {
|
|
69
|
-
throw new Error(`'${key}' must be a string`);
|
|
70
|
-
}
|
|
71
|
-
return value;
|
|
72
|
-
}
|
|
73
|
-
export function optionalNumber(input, key, fallback) {
|
|
74
|
-
const value = input[key];
|
|
75
|
-
if (value === undefined) {
|
|
76
|
-
return fallback;
|
|
77
|
-
}
|
|
78
|
-
if (typeof value !== 'number' || Number.isNaN(value)) {
|
|
79
|
-
throw new Error(`'${key}' must be a valid number`);
|
|
80
|
-
}
|
|
81
|
-
return value;
|
|
82
|
-
}
|
|
83
|
-
export function optionalBoolean(input, key, fallback) {
|
|
84
|
-
const value = input[key];
|
|
85
|
-
if (value === undefined) {
|
|
86
|
-
return fallback;
|
|
87
|
-
}
|
|
88
|
-
if (typeof value !== 'boolean') {
|
|
89
|
-
throw new Error(`'${key}' must be a boolean`);
|
|
90
|
-
}
|
|
91
|
-
return value;
|
|
92
|
-
}
|
|
93
|
-
export function optionalStringArray(input, key) {
|
|
94
|
-
const value = input[key];
|
|
95
|
-
if (value === undefined) {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
if (!Array.isArray(value)) {
|
|
99
|
-
throw new Error(`'${key}' must be an array of strings`);
|
|
100
|
-
}
|
|
101
|
-
const output = [];
|
|
102
|
-
for (const item of value) {
|
|
103
|
-
if (typeof item !== 'string') {
|
|
104
|
-
throw new Error(`'${key}' must contain only strings`);
|
|
105
|
-
}
|
|
106
|
-
output.push(item);
|
|
107
|
-
}
|
|
108
|
-
return output;
|
|
109
|
-
}
|
|
110
|
-
export function optionalJsonObject(input, key) {
|
|
111
|
-
const value = input[key];
|
|
112
|
-
if (value === undefined) {
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
if (!isJsonObject(value)) {
|
|
116
|
-
throw new Error(`'${key}' must be a JSON object`);
|
|
117
|
-
}
|
|
118
|
-
return value;
|
|
119
|
-
}
|
|
120
|
-
export function resolveFromWorkdir(workdir, targetPath) {
|
|
121
|
-
if (targetPath.startsWith('/')) {
|
|
122
|
-
return targetPath;
|
|
123
|
-
}
|
|
124
|
-
return join(workdir, targetPath);
|
|
125
|
-
}
|
|
126
|
-
function isTextPart(part) {
|
|
127
|
-
return (typeof part === 'object' &&
|
|
128
|
-
part !== null &&
|
|
129
|
-
'type' in part &&
|
|
130
|
-
part.type === 'text' &&
|
|
131
|
-
'text' in part &&
|
|
132
|
-
typeof part.text === 'string');
|
|
133
|
-
}
|
|
134
|
-
export function estimateMessageLength(message) {
|
|
135
|
-
const content = message.data.content;
|
|
136
|
-
if (typeof content === 'string') {
|
|
137
|
-
return content.length;
|
|
138
|
-
}
|
|
139
|
-
if (!Array.isArray(content)) {
|
|
140
|
-
return 0;
|
|
141
|
-
}
|
|
142
|
-
let total = 0;
|
|
143
|
-
for (const part of content) {
|
|
144
|
-
if (isTextPart(part)) {
|
|
145
|
-
total += part.text.length;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return total;
|
|
149
|
-
}
|
|
150
|
-
export function truncateString(text, maxLength) {
|
|
151
|
-
if (maxLength < 1) {
|
|
152
|
-
return '';
|
|
153
|
-
}
|
|
154
|
-
if (text.length <= maxLength) {
|
|
155
|
-
return text;
|
|
156
|
-
}
|
|
157
|
-
return text.slice(0, maxLength);
|
|
158
|
-
}
|
|
159
|
-
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { BasicSystemPrompt } from "../extensions/basic-system-prompt.js";
|
|
3
|
-
import type {
|
|
4
|
-
ExtensionApi,
|
|
5
|
-
TurnMiddleware,
|
|
6
|
-
TurnContext,
|
|
7
|
-
TurnResult,
|
|
8
|
-
ConversationState,
|
|
9
|
-
Message,
|
|
10
|
-
MessageEvent,
|
|
11
|
-
} from "@goondan/openharness-types";
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Helpers
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
function makeMockConversationState(): ConversationState & { emitted: MessageEvent[] } {
|
|
18
|
-
const emitted: MessageEvent[] = [];
|
|
19
|
-
const messages: Message[] = [];
|
|
20
|
-
return {
|
|
21
|
-
messages,
|
|
22
|
-
events: [],
|
|
23
|
-
emitted,
|
|
24
|
-
emit: vi.fn((event: MessageEvent) => {
|
|
25
|
-
emitted.push(event);
|
|
26
|
-
if (event.type === "append") {
|
|
27
|
-
messages.push(event.message);
|
|
28
|
-
}
|
|
29
|
-
}),
|
|
30
|
-
restore: vi.fn(),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function makeMockApi(conversation: ConversationState): {
|
|
35
|
-
api: ExtensionApi;
|
|
36
|
-
registeredMiddleware: Array<{ level: string; handler: TurnMiddleware; options?: { priority?: number } }>;
|
|
37
|
-
} {
|
|
38
|
-
const registeredMiddleware: Array<{ level: string; handler: TurnMiddleware; options?: { priority?: number } }> = [];
|
|
39
|
-
|
|
40
|
-
const api: ExtensionApi = {
|
|
41
|
-
pipeline: {
|
|
42
|
-
register: vi.fn((level: string, handler: TurnMiddleware, options?: { priority?: number }) => {
|
|
43
|
-
registeredMiddleware.push({ level, handler, options });
|
|
44
|
-
}) as unknown as ExtensionApi["pipeline"]["register"],
|
|
45
|
-
},
|
|
46
|
-
tools: {
|
|
47
|
-
register: vi.fn(),
|
|
48
|
-
remove: vi.fn(),
|
|
49
|
-
list: vi.fn(() => []),
|
|
50
|
-
},
|
|
51
|
-
on: vi.fn(),
|
|
52
|
-
conversation,
|
|
53
|
-
runtime: {
|
|
54
|
-
agent: {
|
|
55
|
-
name: "test-agent",
|
|
56
|
-
model: { provider: "openai", model: "gpt-4o" },
|
|
57
|
-
extensions: [],
|
|
58
|
-
tools: [],
|
|
59
|
-
},
|
|
60
|
-
agents: {},
|
|
61
|
-
connections: {},
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return { api, registeredMiddleware };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function makeTurnContext(conversation: ConversationState): TurnContext {
|
|
69
|
-
return {
|
|
70
|
-
turnId: "turn-1",
|
|
71
|
-
agentName: "test-agent",
|
|
72
|
-
conversationId: "conv-1",
|
|
73
|
-
conversation,
|
|
74
|
-
abortSignal: new AbortController().signal,
|
|
75
|
-
input: {
|
|
76
|
-
name: "test-event",
|
|
77
|
-
content: [{ type: "text", text: "hello" }],
|
|
78
|
-
properties: {},
|
|
79
|
-
source: {
|
|
80
|
-
connector: "test-connector",
|
|
81
|
-
connectionName: "test",
|
|
82
|
-
receivedAt: new Date().toISOString(),
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
llm: { chat: vi.fn().mockResolvedValue({ text: "mock" }) },
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const stubTurnResult: TurnResult = {
|
|
90
|
-
turnId: "turn-1",
|
|
91
|
-
agentName: "test-agent",
|
|
92
|
-
conversationId: "conv-1",
|
|
93
|
-
status: "completed",
|
|
94
|
-
steps: [],
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
// Tests
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
|
|
101
|
-
describe("BasicSystemPrompt", () => {
|
|
102
|
-
it("creates an Extension with name 'basic-system-prompt'", () => {
|
|
103
|
-
const ext = BasicSystemPrompt("You are helpful.");
|
|
104
|
-
expect(ext.name).toBe("basic-system-prompt");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("register() calls api.pipeline.register with level 'turn'", () => {
|
|
108
|
-
const conversation = makeMockConversationState();
|
|
109
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
110
|
-
|
|
111
|
-
const ext = BasicSystemPrompt("You are helpful.");
|
|
112
|
-
ext.register(api);
|
|
113
|
-
|
|
114
|
-
expect(api.pipeline.register).toHaveBeenCalledOnce();
|
|
115
|
-
expect(registeredMiddleware).toHaveLength(1);
|
|
116
|
-
expect(registeredMiddleware[0].level).toBe("turn");
|
|
117
|
-
expect(typeof registeredMiddleware[0].handler).toBe("function");
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("registers turn middleware with priority 10 (high priority)", () => {
|
|
121
|
-
const conversation = makeMockConversationState();
|
|
122
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
123
|
-
|
|
124
|
-
const ext = BasicSystemPrompt("You are helpful.");
|
|
125
|
-
ext.register(api);
|
|
126
|
-
|
|
127
|
-
expect(registeredMiddleware[0].options?.priority).toBe(10);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("middleware appends a system message and calls next()", async () => {
|
|
131
|
-
const conversation = makeMockConversationState();
|
|
132
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
133
|
-
|
|
134
|
-
const ext = BasicSystemPrompt("You are helpful.");
|
|
135
|
-
ext.register(api);
|
|
136
|
-
|
|
137
|
-
const middleware = registeredMiddleware[0].handler;
|
|
138
|
-
const ctx = makeTurnContext(conversation);
|
|
139
|
-
const next = vi.fn(async () => stubTurnResult);
|
|
140
|
-
|
|
141
|
-
const result = await middleware(ctx, next);
|
|
142
|
-
|
|
143
|
-
expect(next).toHaveBeenCalledOnce();
|
|
144
|
-
expect(result).toBe(stubTurnResult);
|
|
145
|
-
|
|
146
|
-
// Verify system message was emitted
|
|
147
|
-
expect(conversation.emit).toHaveBeenCalledOnce();
|
|
148
|
-
const emittedEvent = conversation.emitted[0];
|
|
149
|
-
expect(emittedEvent.type).toBe("append");
|
|
150
|
-
if (emittedEvent.type === "append") {
|
|
151
|
-
expect(emittedEvent.message.id).toBe("sys-basic-system-prompt");
|
|
152
|
-
expect(emittedEvent.message.data.role).toBe("system");
|
|
153
|
-
expect(emittedEvent.message.data.content).toBe("You are helpful.");
|
|
154
|
-
expect(emittedEvent.message.metadata?.__createdBy).toBe("basic-system-prompt");
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("does not duplicate the system message on subsequent turns", async () => {
|
|
159
|
-
const conversation = makeMockConversationState();
|
|
160
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
161
|
-
|
|
162
|
-
const ext = BasicSystemPrompt("You are helpful.");
|
|
163
|
-
ext.register(api);
|
|
164
|
-
|
|
165
|
-
const middleware = registeredMiddleware[0].handler;
|
|
166
|
-
const next = vi.fn(async () => stubTurnResult);
|
|
167
|
-
|
|
168
|
-
// First turn — should append
|
|
169
|
-
const ctx1 = makeTurnContext(conversation);
|
|
170
|
-
await middleware(ctx1, next);
|
|
171
|
-
expect(conversation.emitted).toHaveLength(1);
|
|
172
|
-
|
|
173
|
-
// Second turn — system message already in conversation.messages, should skip
|
|
174
|
-
const ctx2 = makeTurnContext(conversation);
|
|
175
|
-
await middleware(ctx2, next);
|
|
176
|
-
expect(conversation.emitted).toHaveLength(1); // still 1, no new append
|
|
177
|
-
|
|
178
|
-
// Third turn — still no duplication
|
|
179
|
-
const ctx3 = makeTurnContext(conversation);
|
|
180
|
-
await middleware(ctx3, next);
|
|
181
|
-
expect(conversation.emitted).toHaveLength(1);
|
|
182
|
-
|
|
183
|
-
// next() should have been called every turn
|
|
184
|
-
expect(next).toHaveBeenCalledTimes(3);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { CompactionSummarize } from "../extensions/compaction-summarize.js";
|
|
3
|
-
import type {
|
|
4
|
-
ExtensionApi,
|
|
5
|
-
StepMiddleware,
|
|
6
|
-
StepContext,
|
|
7
|
-
StepResult,
|
|
8
|
-
ConversationState,
|
|
9
|
-
Message,
|
|
10
|
-
MessageEvent,
|
|
11
|
-
} from "@goondan/openharness-types";
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Helpers
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
function makeMessages(count: number): Message[] {
|
|
18
|
-
return Array.from({ length: count }, (_, i) => ({
|
|
19
|
-
id: `msg-${i}`,
|
|
20
|
-
data: {
|
|
21
|
-
role: "user" as const,
|
|
22
|
-
content: `Message ${i}`,
|
|
23
|
-
},
|
|
24
|
-
}));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function makeMockConversationState(
|
|
28
|
-
messages: Message[],
|
|
29
|
-
): ConversationState & { emitted: MessageEvent[] } {
|
|
30
|
-
const emitted: MessageEvent[] = [];
|
|
31
|
-
return {
|
|
32
|
-
messages,
|
|
33
|
-
events: [],
|
|
34
|
-
emitted,
|
|
35
|
-
emit: vi.fn((event: MessageEvent) => {
|
|
36
|
-
emitted.push(event);
|
|
37
|
-
}),
|
|
38
|
-
restore: vi.fn(),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function makeMockApi(conversation: ConversationState): {
|
|
43
|
-
api: ExtensionApi;
|
|
44
|
-
registeredMiddleware: Array<{
|
|
45
|
-
level: string;
|
|
46
|
-
handler: StepMiddleware;
|
|
47
|
-
options?: { priority?: number };
|
|
48
|
-
}>;
|
|
49
|
-
} {
|
|
50
|
-
const registeredMiddleware: Array<{
|
|
51
|
-
level: string;
|
|
52
|
-
handler: StepMiddleware;
|
|
53
|
-
options?: { priority?: number };
|
|
54
|
-
}> = [];
|
|
55
|
-
|
|
56
|
-
const api: ExtensionApi = {
|
|
57
|
-
pipeline: {
|
|
58
|
-
register: vi.fn(
|
|
59
|
-
(level: string, handler: StepMiddleware, options?: { priority?: number }) => {
|
|
60
|
-
registeredMiddleware.push({ level, handler, options });
|
|
61
|
-
},
|
|
62
|
-
) as unknown as ExtensionApi["pipeline"]["register"],
|
|
63
|
-
},
|
|
64
|
-
tools: {
|
|
65
|
-
register: vi.fn(),
|
|
66
|
-
remove: vi.fn(),
|
|
67
|
-
list: vi.fn(() => []),
|
|
68
|
-
},
|
|
69
|
-
on: vi.fn(),
|
|
70
|
-
conversation,
|
|
71
|
-
runtime: {
|
|
72
|
-
agent: {
|
|
73
|
-
name: "test-agent",
|
|
74
|
-
model: { provider: "openai", model: "gpt-4o" },
|
|
75
|
-
extensions: [],
|
|
76
|
-
tools: [],
|
|
77
|
-
},
|
|
78
|
-
agents: {},
|
|
79
|
-
connections: {},
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return { api, registeredMiddleware };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function makeMockLlmClient() {
|
|
87
|
-
return {
|
|
88
|
-
chat: vi.fn().mockResolvedValue({ text: "LLM-generated summary of the conversation." }),
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function makeStepContext(conversation: ConversationState): StepContext {
|
|
93
|
-
return {
|
|
94
|
-
turnId: "turn-1",
|
|
95
|
-
agentName: "test-agent",
|
|
96
|
-
conversationId: "conv-1",
|
|
97
|
-
conversation,
|
|
98
|
-
stepNumber: 1,
|
|
99
|
-
abortSignal: new AbortController().signal,
|
|
100
|
-
input: {
|
|
101
|
-
name: "test-event",
|
|
102
|
-
content: [{ type: "text", text: "hello" }],
|
|
103
|
-
properties: {},
|
|
104
|
-
source: {
|
|
105
|
-
connector: "test-connector",
|
|
106
|
-
connectionName: "test",
|
|
107
|
-
receivedAt: new Date().toISOString(),
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
llm: makeMockLlmClient(),
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const stubStepResult: StepResult = {
|
|
115
|
-
toolCalls: [],
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// Tests
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
describe("CompactionSummarize", () => {
|
|
123
|
-
it("creates an Extension with name 'compaction-summarize'", () => {
|
|
124
|
-
const ext = CompactionSummarize({ threshold: 10 });
|
|
125
|
-
expect(ext.name).toBe("compaction-summarize");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("registers step middleware via api.pipeline.register", () => {
|
|
129
|
-
const conversation = makeMockConversationState([]);
|
|
130
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
131
|
-
|
|
132
|
-
const ext = CompactionSummarize({ threshold: 10 });
|
|
133
|
-
ext.register(api);
|
|
134
|
-
|
|
135
|
-
expect(api.pipeline.register).toHaveBeenCalledOnce();
|
|
136
|
-
expect(registeredMiddleware[0].level).toBe("step");
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("does NOT compact when messages are below threshold", async () => {
|
|
140
|
-
const messages = makeMessages(5);
|
|
141
|
-
const conversation = makeMockConversationState(messages);
|
|
142
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
143
|
-
|
|
144
|
-
const ext = CompactionSummarize({ threshold: 10 });
|
|
145
|
-
ext.register(api);
|
|
146
|
-
|
|
147
|
-
const middleware = registeredMiddleware[0].handler;
|
|
148
|
-
const ctx = makeStepContext(conversation);
|
|
149
|
-
const next = vi.fn(async () => stubStepResult);
|
|
150
|
-
|
|
151
|
-
await middleware(ctx, next);
|
|
152
|
-
|
|
153
|
-
expect(conversation.emit).not.toHaveBeenCalled();
|
|
154
|
-
expect(next).toHaveBeenCalledOnce();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("compacts when messages exceed threshold", async () => {
|
|
158
|
-
const messages = makeMessages(12);
|
|
159
|
-
const conversation = makeMockConversationState(messages);
|
|
160
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
161
|
-
|
|
162
|
-
const ext = CompactionSummarize({ threshold: 10 });
|
|
163
|
-
ext.register(api);
|
|
164
|
-
|
|
165
|
-
const middleware = registeredMiddleware[0].handler;
|
|
166
|
-
const ctx = makeStepContext(conversation);
|
|
167
|
-
const next = vi.fn(async () => stubStepResult);
|
|
168
|
-
|
|
169
|
-
await middleware(ctx, next);
|
|
170
|
-
|
|
171
|
-
const emitted = (conversation as ReturnType<typeof makeMockConversationState>).emitted;
|
|
172
|
-
|
|
173
|
-
// keepCount = floor(10/2) = 5, removeCount = 12 - 5 = 7
|
|
174
|
-
// Should have 7 remove events + 1 append event
|
|
175
|
-
const removeEvents = emitted.filter((e) => e.type === "remove");
|
|
176
|
-
const appendEvents = emitted.filter((e) => e.type === "append");
|
|
177
|
-
|
|
178
|
-
expect(removeEvents).toHaveLength(7);
|
|
179
|
-
expect(appendEvents).toHaveLength(1);
|
|
180
|
-
|
|
181
|
-
// The appended message should be a system summary
|
|
182
|
-
const appendEvent = appendEvents[0];
|
|
183
|
-
if (appendEvent.type === "append") {
|
|
184
|
-
expect(appendEvent.message.data.role).toBe("system");
|
|
185
|
-
expect(appendEvent.message.data.content).toContain("[Summary of earlier conversation]:");
|
|
186
|
-
expect(appendEvent.message.metadata?.__createdBy).toBe("compaction-summarize");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
expect(next).toHaveBeenCalledOnce();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("removes the oldest messages (not the newest)", async () => {
|
|
193
|
-
const messages = makeMessages(12);
|
|
194
|
-
const conversation = makeMockConversationState(messages);
|
|
195
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
196
|
-
|
|
197
|
-
const ext = CompactionSummarize({ threshold: 10 });
|
|
198
|
-
ext.register(api);
|
|
199
|
-
|
|
200
|
-
const middleware = registeredMiddleware[0].handler;
|
|
201
|
-
const ctx = makeStepContext(conversation);
|
|
202
|
-
const next = vi.fn(async () => stubStepResult);
|
|
203
|
-
|
|
204
|
-
await middleware(ctx, next);
|
|
205
|
-
|
|
206
|
-
const emitted = (conversation as ReturnType<typeof makeMockConversationState>).emitted;
|
|
207
|
-
const removeEvents = emitted.filter(
|
|
208
|
-
(e): e is Extract<typeof e, { type: "remove" }> => e.type === "remove",
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
// Should remove msg-0 through msg-6 (the oldest 7)
|
|
212
|
-
const removedIds = removeEvents.map((e) => e.messageId);
|
|
213
|
-
expect(removedIds).toContain("msg-0");
|
|
214
|
-
expect(removedIds).toContain("msg-6");
|
|
215
|
-
expect(removedIds).not.toContain("msg-7");
|
|
216
|
-
expect(removedIds).not.toContain("msg-11");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("summary content includes text from removed messages", async () => {
|
|
220
|
-
const messages = makeMessages(12);
|
|
221
|
-
const conversation = makeMockConversationState(messages);
|
|
222
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
223
|
-
|
|
224
|
-
const ext = CompactionSummarize({ threshold: 10 });
|
|
225
|
-
ext.register(api);
|
|
226
|
-
|
|
227
|
-
const middleware = registeredMiddleware[0].handler;
|
|
228
|
-
const ctx = makeStepContext(conversation);
|
|
229
|
-
const next = vi.fn(async () => stubStepResult);
|
|
230
|
-
|
|
231
|
-
await middleware(ctx, next);
|
|
232
|
-
|
|
233
|
-
// Verify LLM was called for summarization
|
|
234
|
-
expect(ctx.llm.chat).toHaveBeenCalledOnce();
|
|
235
|
-
|
|
236
|
-
const emitted = (conversation as ReturnType<typeof makeMockConversationState>).emitted;
|
|
237
|
-
const appendEvent = emitted.find((e) => e.type === "append");
|
|
238
|
-
if (appendEvent && appendEvent.type === "append") {
|
|
239
|
-
const content = appendEvent.message.data.content as string;
|
|
240
|
-
// Should include the LLM-generated summary
|
|
241
|
-
expect(content).toContain("LLM-generated summary of the conversation.");
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("uses custom summarizer when provided", async () => {
|
|
246
|
-
const messages = makeMessages(12);
|
|
247
|
-
const conversation = makeMockConversationState(messages);
|
|
248
|
-
const { api, registeredMiddleware } = makeMockApi(conversation);
|
|
249
|
-
|
|
250
|
-
const summarizer = vi.fn(async (msgs: Message[]) => {
|
|
251
|
-
return `Custom summary of ${msgs.length} messages`;
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
const ext = CompactionSummarize({ threshold: 10, summarizer });
|
|
255
|
-
ext.register(api);
|
|
256
|
-
|
|
257
|
-
const middleware = registeredMiddleware[0].handler;
|
|
258
|
-
const ctx = makeStepContext(conversation);
|
|
259
|
-
const next = vi.fn(async () => stubStepResult);
|
|
260
|
-
|
|
261
|
-
await middleware(ctx, next);
|
|
262
|
-
|
|
263
|
-
// summarizer should have been called with the 7 removed messages
|
|
264
|
-
expect(summarizer).toHaveBeenCalledOnce();
|
|
265
|
-
expect(summarizer).toHaveBeenCalledWith(
|
|
266
|
-
expect.arrayContaining([
|
|
267
|
-
expect.objectContaining({ id: "msg-0" }),
|
|
268
|
-
expect.objectContaining({ id: "msg-6" }),
|
|
269
|
-
]),
|
|
270
|
-
);
|
|
271
|
-
expect(summarizer.mock.calls[0][0]).toHaveLength(7);
|
|
272
|
-
|
|
273
|
-
const emitted = (conversation as ReturnType<typeof makeMockConversationState>).emitted;
|
|
274
|
-
const appendEvent = emitted.find((e) => e.type === "append");
|
|
275
|
-
if (appendEvent && appendEvent.type === "append") {
|
|
276
|
-
const content = appendEvent.message.data.content as string;
|
|
277
|
-
expect(content).toContain("Custom summary of 7 messages");
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
expect(next).toHaveBeenCalledOnce();
|
|
281
|
-
});
|
|
282
|
-
});
|