@goondan/openharness-base 0.1.1 → 0.1.3

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/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { Extension, ToolDefinition } from '@goondan/openharness-types';
2
2
 
3
3
  /**
4
- * ContextMessage extension — prepends a system message to the conversation
4
+ * BasicSystemPrompt extension — prepends a system message to the conversation
5
5
  * at the start of every turn.
6
6
  *
7
7
  * Priority 10 (HIGH) ensures it runs before other turn middleware.
8
8
  */
9
- declare function ContextMessage(text: string): Extension;
9
+ declare function BasicSystemPrompt(text: string): Extension;
10
10
 
11
11
  /**
12
12
  * MessageWindow extension — truncates conversation history to keep only
@@ -69,4 +69,4 @@ interface WaitToolConfig {
69
69
  }
70
70
  declare function WaitTool(config?: WaitToolConfig): ToolDefinition;
71
71
 
72
- export { BashTool, type BashToolConfig, CompactionSummarize, ContextMessage, FileListTool, FileReadTool, FileWriteTool, HttpFetchTool, JsonQueryTool, Logging, MessageWindow, RequiredToolsGuard, TextTransformTool, ToolSearch, WaitTool, type WaitToolConfig };
72
+ export { BashTool, type BashToolConfig, BasicSystemPrompt, CompactionSummarize, FileListTool, FileReadTool, FileWriteTool, HttpFetchTool, JsonQueryTool, Logging, MessageWindow, RequiredToolsGuard, TextTransformTool, ToolSearch, WaitTool, type WaitToolConfig };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- // src/extensions/context-message.ts
1
+ // src/extensions/basic-system-prompt.ts
2
2
  import { randomUUID } from "crypto";
3
- function ContextMessage(text) {
3
+ function BasicSystemPrompt(text) {
4
4
  return {
5
- name: "context-message",
5
+ name: "basic-system-prompt",
6
6
  register(api) {
7
7
  api.pipeline.register(
8
8
  "turn",
@@ -10,13 +10,13 @@ function ContextMessage(text) {
10
10
  ctx.conversation.emit({
11
11
  type: "append",
12
12
  message: {
13
- id: `ctx-msg-${randomUUID()}`,
13
+ id: `sys-${randomUUID()}`,
14
14
  data: {
15
15
  role: "system",
16
16
  content: text
17
17
  },
18
18
  metadata: {
19
- __createdBy: "context-message"
19
+ __createdBy: "basic-system-prompt"
20
20
  }
21
21
  }
22
22
  });
@@ -490,8 +490,8 @@ function WaitTool(config = {}) {
490
490
  }
491
491
  export {
492
492
  BashTool,
493
+ BasicSystemPrompt,
493
494
  CompactionSummarize,
494
- ContextMessage,
495
495
  FileListTool,
496
496
  FileReadTool,
497
497
  FileWriteTool,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goondan/openharness-base",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -9,7 +9,7 @@
9
9
  }
10
10
  },
11
11
  "dependencies": {
12
- "@goondan/openharness-types": "0.1.1"
12
+ "@goondan/openharness-types": "0.1.3"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^25.5.0",
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi } from "vitest";
2
- import { ContextMessage } from "../extensions/context-message.js";
2
+ import { BasicSystemPrompt } from "../extensions/basic-system-prompt.js";
3
3
  import type {
4
4
  ExtensionApi,
5
5
  TurnMiddleware,
@@ -97,19 +97,17 @@ const stubTurnResult: TurnResult = {
97
97
  // Tests
98
98
  // ---------------------------------------------------------------------------
99
99
 
100
- describe("ContextMessage", () => {
101
- // Test 1: creates Extension with correct name
102
- it("creates an Extension with name 'context-message'", () => {
103
- const ext = ContextMessage("You are a helpful assistant.");
104
- expect(ext.name).toBe("context-message");
100
+ describe("BasicSystemPrompt", () => {
101
+ it("creates an Extension with name 'basic-system-prompt'", () => {
102
+ const ext = BasicSystemPrompt("You are helpful.");
103
+ expect(ext.name).toBe("basic-system-prompt");
105
104
  });
106
105
 
107
- // Test 2: register() adds turn middleware via api.pipeline.register
108
106
  it("register() calls api.pipeline.register with level 'turn'", () => {
109
107
  const conversation = makeMockConversationState();
110
108
  const { api, registeredMiddleware } = makeMockApi(conversation);
111
109
 
112
- const ext = ContextMessage("Hello");
110
+ const ext = BasicSystemPrompt("You are helpful.");
113
111
  ext.register(api);
114
112
 
115
113
  expect(api.pipeline.register).toHaveBeenCalledOnce();
@@ -118,49 +116,21 @@ describe("ContextMessage", () => {
118
116
  expect(typeof registeredMiddleware[0].handler).toBe("function");
119
117
  });
120
118
 
121
- // Test 3: middleware is registered with HIGH priority (low number)
122
119
  it("registers turn middleware with priority 10 (high priority)", () => {
123
120
  const conversation = makeMockConversationState();
124
121
  const { api, registeredMiddleware } = makeMockApi(conversation);
125
122
 
126
- const ext = ContextMessage("Hello");
123
+ const ext = BasicSystemPrompt("You are helpful.");
127
124
  ext.register(api);
128
125
 
129
126
  expect(registeredMiddleware[0].options?.priority).toBe(10);
130
127
  });
131
128
 
132
- // Test 4: middleware prepends system message to conversation and text matches
133
- it("middleware emits append event with system message matching the text argument", async () => {
129
+ it("middleware appends a system message and calls next()", async () => {
134
130
  const conversation = makeMockConversationState();
135
131
  const { api, registeredMiddleware } = makeMockApi(conversation);
136
- const systemText = "You are a helpful assistant.";
137
132
 
138
- const ext = ContextMessage(systemText);
139
- ext.register(api);
140
-
141
- const middleware = registeredMiddleware[0].handler;
142
- const ctx = makeTurnContext(conversation);
143
- const next = vi.fn(async () => stubTurnResult);
144
-
145
- await middleware(ctx, next);
146
-
147
- expect(conversation.emit).toHaveBeenCalledOnce();
148
- const emitted = conversation.emitted[0];
149
- expect(emitted.type).toBe("append");
150
- if (emitted.type === "append") {
151
- expect(emitted.message.data.role).toBe("system");
152
- expect(emitted.message.data.content).toBe(systemText);
153
- expect(emitted.message.metadata?.__createdBy).toBe("context-message");
154
- expect(typeof emitted.message.id).toBe("string");
155
- }
156
- });
157
-
158
- // Test 5: middleware calls next() after emitting
159
- it("middleware calls next() after emitting the system message", async () => {
160
- const conversation = makeMockConversationState();
161
- const { api, registeredMiddleware } = makeMockApi(conversation);
162
-
163
- const ext = ContextMessage("Some context");
133
+ const ext = BasicSystemPrompt("You are helpful.");
164
134
  ext.register(api);
165
135
 
166
136
  const middleware = registeredMiddleware[0].handler;
@@ -171,33 +141,15 @@ describe("ContextMessage", () => {
171
141
 
172
142
  expect(next).toHaveBeenCalledOnce();
173
143
  expect(result).toBe(stubTurnResult);
174
- });
175
-
176
- // Test 6: multiple ContextMessage extensions → multiple system messages
177
- it("multiple ContextMessage extensions each prepend their own system message", async () => {
178
- const conversation = makeMockConversationState();
179
- const { api: api1, registeredMiddleware: rm1 } = makeMockApi(conversation);
180
- const { api: api2, registeredMiddleware: rm2 } = makeMockApi(conversation);
181
-
182
- const ext1 = ContextMessage("First context");
183
- const ext2 = ContextMessage("Second context");
184
144
 
185
- ext1.register(api1);
186
- ext2.register(api2);
187
-
188
- const ctx = makeTurnContext(conversation);
189
- const next = vi.fn(async () => stubTurnResult);
190
-
191
- await rm1[0].handler(ctx, next);
192
- await rm2[0].handler(ctx, next);
193
-
194
- expect(conversation.emitted).toHaveLength(2);
195
-
196
- const texts = conversation.emitted
197
- .filter((e): e is Extract<typeof e, { type: "append" }> => e.type === "append")
198
- .map((e) => e.message.data.content);
199
-
200
- expect(texts).toContain("First context");
201
- expect(texts).toContain("Second context");
145
+ // Verify system message was emitted
146
+ expect(conversation.emit).toHaveBeenCalledOnce();
147
+ const emittedEvent = conversation.emitted[0];
148
+ expect(emittedEvent.type).toBe("append");
149
+ if (emittedEvent.type === "append") {
150
+ expect(emittedEvent.message.data.role).toBe("system");
151
+ expect(emittedEvent.message.data.content).toBe("You are helpful.");
152
+ expect(emittedEvent.message.metadata?.__createdBy).toBe("basic-system-prompt");
153
+ }
202
154
  });
203
155
  });
@@ -2,14 +2,14 @@ import type { Extension, ExtensionApi } from "@goondan/openharness-types";
2
2
  import { randomUUID } from "node:crypto";
3
3
 
4
4
  /**
5
- * ContextMessage extension — prepends a system message to the conversation
5
+ * BasicSystemPrompt extension — prepends a system message to the conversation
6
6
  * at the start of every turn.
7
7
  *
8
8
  * Priority 10 (HIGH) ensures it runs before other turn middleware.
9
9
  */
10
- export function ContextMessage(text: string): Extension {
10
+ export function BasicSystemPrompt(text: string): Extension {
11
11
  return {
12
- name: "context-message",
12
+ name: "basic-system-prompt",
13
13
 
14
14
  register(api: ExtensionApi): void {
15
15
  api.pipeline.register(
@@ -18,13 +18,13 @@ export function ContextMessage(text: string): Extension {
18
18
  ctx.conversation.emit({
19
19
  type: "append",
20
20
  message: {
21
- id: `ctx-msg-${randomUUID()}`,
21
+ id: `sys-${randomUUID()}`,
22
22
  data: {
23
23
  role: "system",
24
24
  content: text,
25
25
  },
26
26
  metadata: {
27
- __createdBy: "context-message",
27
+ __createdBy: "basic-system-prompt",
28
28
  },
29
29
  },
30
30
  });
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextMessage } from "./extensions/context-message.js";
1
+ export { BasicSystemPrompt } from "./extensions/basic-system-prompt.js";
2
2
  export { MessageWindow } from "./extensions/message-window.js";
3
3
  export { CompactionSummarize } from "./extensions/compaction-summarize.js";
4
4
  export { Logging } from "./extensions/logging.js";
@@ -1,8 +0,0 @@
1
- import type { Message } from '../types.js';
2
- /**
3
- * Removes messages that would leave invalid tool_result references after trimming.
4
- * Anthropic requires every tool_result block to map to a tool_use/tool-call block
5
- * in the immediately previous assistant message.
6
- */
7
- export declare function normalizeRemovalTargets(messages: Message[], initialRemovedIds: ReadonlySet<string>): Set<string>;
8
- //# sourceMappingURL=message-integrity.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-integrity.d.ts","sourceRoot":"","sources":["../../src/extensions/message-integrity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAiF3C;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,OAAO,EAAE,EACnB,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,GACrC,GAAG,CAAC,MAAM,CAAC,CAwBb"}
@@ -1,88 +0,0 @@
1
- function readToolCallId(part) {
2
- const value = part.toolCallId;
3
- if (typeof value !== 'string' || value.length === 0) {
4
- return null;
5
- }
6
- return value;
7
- }
8
- function collectToolCallIds(message) {
9
- const ids = new Set();
10
- if (!message) {
11
- return ids;
12
- }
13
- const content = message.data.content;
14
- if (!Array.isArray(content)) {
15
- return ids;
16
- }
17
- for (const part of content) {
18
- if (part.type !== 'tool-call') {
19
- continue;
20
- }
21
- const toolCallId = readToolCallId(part);
22
- if (!toolCallId) {
23
- continue;
24
- }
25
- ids.add(toolCallId);
26
- }
27
- return ids;
28
- }
29
- function collectToolResultIds(message) {
30
- const content = message.data.content;
31
- if (!Array.isArray(content)) {
32
- return [];
33
- }
34
- const ids = [];
35
- for (const part of content) {
36
- if (part.type !== 'tool-result') {
37
- continue;
38
- }
39
- const toolCallId = readToolCallId(part);
40
- if (!toolCallId) {
41
- continue;
42
- }
43
- ids.push(toolCallId);
44
- }
45
- return ids;
46
- }
47
- function hasDanglingToolResult(message, previousMessage) {
48
- const toolResultIds = collectToolResultIds(message);
49
- if (toolResultIds.length === 0) {
50
- return false;
51
- }
52
- const previousToolCallIds = collectToolCallIds(previousMessage);
53
- if (previousToolCallIds.size === 0) {
54
- return true;
55
- }
56
- for (const toolResultId of toolResultIds) {
57
- if (!previousToolCallIds.has(toolResultId)) {
58
- return true;
59
- }
60
- }
61
- return false;
62
- }
63
- /**
64
- * Removes messages that would leave invalid tool_result references after trimming.
65
- * Anthropic requires every tool_result block to map to a tool_use/tool-call block
66
- * in the immediately previous assistant message.
67
- */
68
- export function normalizeRemovalTargets(messages, initialRemovedIds) {
69
- const removedIds = new Set(initialRemovedIds);
70
- let changed = true;
71
- while (changed) {
72
- changed = false;
73
- let previousRemainingMessage;
74
- for (const message of messages) {
75
- if (removedIds.has(message.id)) {
76
- continue;
77
- }
78
- if (hasDanglingToolResult(message, previousRemainingMessage)) {
79
- removedIds.add(message.id);
80
- changed = true;
81
- continue;
82
- }
83
- previousRemainingMessage = message;
84
- }
85
- }
86
- return removedIds;
87
- }
88
- //# sourceMappingURL=message-integrity.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-integrity.js","sourceRoot":"","sources":["../../src/extensions/message-integrity.ts"],"names":[],"mappings":"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"}