@clawpify/skills 1.0.7 → 1.0.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/dist/agent.d.ts +13 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +43 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -1
- package/dist/memory.d.ts +8 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +3 -0
- package/dist/prompt-utils.d.ts +7 -0
- package/dist/prompt-utils.d.ts.map +1 -0
- package/package.json +1 -4
- package/src/agent.test.ts +132 -1
- package/src/agent.ts +54 -3
- package/src/index.ts +5 -1
- package/src/memory.ts +13 -0
- package/src/prompt-utils.ts +12 -0
package/dist/agent.d.ts
CHANGED
|
@@ -46,13 +46,25 @@ export interface ThinkingConfig {
|
|
|
46
46
|
/** Token budget for thinking. Must be >= 1024. */
|
|
47
47
|
budgetTokens: number;
|
|
48
48
|
}
|
|
49
|
+
/** Structured context for prompt assembly. Used when systemInstruction is not provided. */
|
|
50
|
+
export interface AgentContextConfig {
|
|
51
|
+
/** Heartbeat content (only for new sessions). Injected at prompt start. */
|
|
52
|
+
heartbeatContent?: string;
|
|
53
|
+
/** Long-term memory content. Always included. */
|
|
54
|
+
memoryContent?: string;
|
|
55
|
+
/** Base system instruction override. Falls back to DEFAULT_SYSTEM_INSTRUCTION. */
|
|
56
|
+
baseInstruction?: string;
|
|
57
|
+
}
|
|
49
58
|
/** Full configuration accepted by the ShopifyAgent constructor. */
|
|
50
59
|
export interface AgentConfig {
|
|
51
60
|
shopify: ShopifyClient;
|
|
52
61
|
skillContent: string;
|
|
53
62
|
model?: string;
|
|
54
|
-
/** Override the default system instruction sent to the model. */
|
|
63
|
+
/** Override the default system instruction sent to the model (raw string). */
|
|
55
64
|
systemInstruction?: string;
|
|
65
|
+
/** Structured context assembled into a system instruction internally.
|
|
66
|
+
* Ignored when systemInstruction is provided. */
|
|
67
|
+
context?: AgentContextConfig;
|
|
56
68
|
/** Override default Claude Sonnet pricing. */
|
|
57
69
|
pricing?: ModelPricing;
|
|
58
70
|
/** Register plugins at construction time. */
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/agent.js
CHANGED
|
@@ -48,6 +48,19 @@ async function collectMarkdown(dir, parts) {
|
|
|
48
48
|
|
|
49
49
|
// src/agent.ts
|
|
50
50
|
import Anthropic from "@anthropic-ai/sdk";
|
|
51
|
+
|
|
52
|
+
// src/prompt-utils.ts
|
|
53
|
+
var MAX_HEARTBEAT_CHARS = 2000;
|
|
54
|
+
var MAX_MEMORY_CHARS = 4000;
|
|
55
|
+
function clampContent(content, maxChars) {
|
|
56
|
+
const trimmed = content.trim();
|
|
57
|
+
if (trimmed.length <= maxChars)
|
|
58
|
+
return trimmed;
|
|
59
|
+
return trimmed.slice(0, maxChars).trimEnd() + `
|
|
60
|
+
...[truncated]`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/agent.ts
|
|
51
64
|
var DEFAULT_SYSTEM_INSTRUCTION = "You're Clawpify. You help run a Shopify store. The merchant texts you to get stuff done";
|
|
52
65
|
var SHOPIFY_GRAPHQL_TOOL = {
|
|
53
66
|
name: "shopify_graphql",
|
|
@@ -128,6 +141,29 @@ function extractContent(response) {
|
|
|
128
141
|
}
|
|
129
142
|
return { text, thinking };
|
|
130
143
|
}
|
|
144
|
+
function buildSystemInstruction(ctx) {
|
|
145
|
+
const base = ctx.baseInstruction ?? DEFAULT_SYSTEM_INSTRUCTION;
|
|
146
|
+
let heartbeatBlock = "";
|
|
147
|
+
if (ctx.heartbeatContent?.trim()) {
|
|
148
|
+
heartbeatBlock = `
|
|
149
|
+
|
|
150
|
+
This is a NEW conversation (no recent chat history). ` + "Follow these instructions first, then respond to the merchant's message. " + `Keep the summary brief (1-2 sentences), then address what they asked.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
` + clampContent(ctx.heartbeatContent, MAX_HEARTBEAT_CHARS) + `
|
|
154
|
+
---`;
|
|
155
|
+
}
|
|
156
|
+
let memoryBlock = "";
|
|
157
|
+
if (ctx.memoryContent?.trim()) {
|
|
158
|
+
memoryBlock = `
|
|
159
|
+
|
|
160
|
+
Things to remember about this merchant/store ` + `(use this context in your responses):
|
|
161
|
+
---
|
|
162
|
+
` + clampContent(ctx.memoryContent, MAX_MEMORY_CHARS) + `
|
|
163
|
+
---`;
|
|
164
|
+
}
|
|
165
|
+
return base + heartbeatBlock + memoryBlock;
|
|
166
|
+
}
|
|
131
167
|
|
|
132
168
|
class ShopifyAgent {
|
|
133
169
|
anthropic;
|
|
@@ -145,7 +181,13 @@ class ShopifyAgent {
|
|
|
145
181
|
this.anthropic = new Anthropic;
|
|
146
182
|
this.shopify = config.shopify;
|
|
147
183
|
this.skillContent = config.skillContent;
|
|
148
|
-
|
|
184
|
+
if (config.systemInstruction != null) {
|
|
185
|
+
this.systemInstruction = config.systemInstruction;
|
|
186
|
+
} else if (config.context) {
|
|
187
|
+
this.systemInstruction = buildSystemInstruction(config.context);
|
|
188
|
+
} else {
|
|
189
|
+
this.systemInstruction = DEFAULT_SYSTEM_INSTRUCTION;
|
|
190
|
+
}
|
|
149
191
|
this.model = config.model ?? "claude-sonnet-4-5";
|
|
150
192
|
this.pricing = config.pricing ?? DEFAULT_PRICING;
|
|
151
193
|
this.hooks = config.hooks ?? {};
|
package/dist/index.d.ts
CHANGED
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
export { ShopifyClient, createShopifyClient } from "./shopify";
|
|
37
37
|
export { createAuthenticatedConfig, getAccessToken, type AccessTokenResponse, type ClientCredentialsConfig, } from "./auth";
|
|
38
38
|
export { ShopifyAgent, DEFAULT_SYSTEM_INSTRUCTION } from "./agent";
|
|
39
|
-
export type { AgentConfig, AgentHooks, AgentPlugin, ChatResult, ModelPricing, StreamEvent, ThinkingConfig, TokenUsage, } from "./agent";
|
|
39
|
+
export type { AgentConfig, AgentContextConfig, AgentHooks, AgentPlugin, ChatResult, ModelPricing, StreamEvent, ThinkingConfig, TokenUsage, } from "./agent";
|
|
40
40
|
export { InMemoryStore } from "./memory";
|
|
41
|
-
export type { MemoryStore } from "./memory";
|
|
41
|
+
export type { MemoryStore, LoadResult } from "./memory";
|
|
42
|
+
export { clampContent, MAX_HEARTBEAT_CHARS, MAX_MEMORY_CHARS } from "./prompt-utils";
|
|
42
43
|
export { loadSkills, loadSkillMetadata, loadSkillReference, listSkillReferences, } from "./skills";
|
|
43
44
|
export type { ShopifyClientConfig, GraphQLResponse } from "./shopify";
|
|
44
45
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACnE,YAAY,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACnE,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGxD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGrF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAGlB,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -137,6 +137,19 @@ async function collectMarkdown(dir, parts) {
|
|
|
137
137
|
|
|
138
138
|
// src/agent.ts
|
|
139
139
|
import Anthropic from "@anthropic-ai/sdk";
|
|
140
|
+
|
|
141
|
+
// src/prompt-utils.ts
|
|
142
|
+
var MAX_HEARTBEAT_CHARS = 2000;
|
|
143
|
+
var MAX_MEMORY_CHARS = 4000;
|
|
144
|
+
function clampContent(content, maxChars) {
|
|
145
|
+
const trimmed = content.trim();
|
|
146
|
+
if (trimmed.length <= maxChars)
|
|
147
|
+
return trimmed;
|
|
148
|
+
return trimmed.slice(0, maxChars).trimEnd() + `
|
|
149
|
+
...[truncated]`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/agent.ts
|
|
140
153
|
var DEFAULT_SYSTEM_INSTRUCTION = "You're Clawpify. You help run a Shopify store. The merchant texts you to get stuff done";
|
|
141
154
|
var SHOPIFY_GRAPHQL_TOOL = {
|
|
142
155
|
name: "shopify_graphql",
|
|
@@ -217,6 +230,29 @@ function extractContent(response) {
|
|
|
217
230
|
}
|
|
218
231
|
return { text, thinking };
|
|
219
232
|
}
|
|
233
|
+
function buildSystemInstruction(ctx) {
|
|
234
|
+
const base = ctx.baseInstruction ?? DEFAULT_SYSTEM_INSTRUCTION;
|
|
235
|
+
let heartbeatBlock = "";
|
|
236
|
+
if (ctx.heartbeatContent?.trim()) {
|
|
237
|
+
heartbeatBlock = `
|
|
238
|
+
|
|
239
|
+
This is a NEW conversation (no recent chat history). ` + "Follow these instructions first, then respond to the merchant's message. " + `Keep the summary brief (1-2 sentences), then address what they asked.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
` + clampContent(ctx.heartbeatContent, MAX_HEARTBEAT_CHARS) + `
|
|
243
|
+
---`;
|
|
244
|
+
}
|
|
245
|
+
let memoryBlock = "";
|
|
246
|
+
if (ctx.memoryContent?.trim()) {
|
|
247
|
+
memoryBlock = `
|
|
248
|
+
|
|
249
|
+
Things to remember about this merchant/store ` + `(use this context in your responses):
|
|
250
|
+
---
|
|
251
|
+
` + clampContent(ctx.memoryContent, MAX_MEMORY_CHARS) + `
|
|
252
|
+
---`;
|
|
253
|
+
}
|
|
254
|
+
return base + heartbeatBlock + memoryBlock;
|
|
255
|
+
}
|
|
220
256
|
|
|
221
257
|
class ShopifyAgent {
|
|
222
258
|
anthropic;
|
|
@@ -234,7 +270,13 @@ class ShopifyAgent {
|
|
|
234
270
|
this.anthropic = new Anthropic;
|
|
235
271
|
this.shopify = config.shopify;
|
|
236
272
|
this.skillContent = config.skillContent;
|
|
237
|
-
|
|
273
|
+
if (config.systemInstruction != null) {
|
|
274
|
+
this.systemInstruction = config.systemInstruction;
|
|
275
|
+
} else if (config.context) {
|
|
276
|
+
this.systemInstruction = buildSystemInstruction(config.context);
|
|
277
|
+
} else {
|
|
278
|
+
this.systemInstruction = DEFAULT_SYSTEM_INSTRUCTION;
|
|
279
|
+
}
|
|
238
280
|
this.model = config.model ?? "claude-sonnet-4-5";
|
|
239
281
|
this.pricing = config.pricing ?? DEFAULT_PRICING;
|
|
240
282
|
this.hooks = config.hooks ?? {};
|
|
@@ -561,6 +603,9 @@ class InMemoryStore {
|
|
|
561
603
|
async clear(sessionId) {
|
|
562
604
|
this.store.delete(sessionId);
|
|
563
605
|
}
|
|
606
|
+
async loadWithStatus(sessionId) {
|
|
607
|
+
return { history: await this.load(sessionId), error: false };
|
|
608
|
+
}
|
|
564
609
|
}
|
|
565
610
|
export {
|
|
566
611
|
loadSkills,
|
|
@@ -570,8 +615,11 @@ export {
|
|
|
570
615
|
getAccessToken,
|
|
571
616
|
createShopifyClient,
|
|
572
617
|
createAuthenticatedConfig,
|
|
618
|
+
clampContent,
|
|
573
619
|
ShopifyClient,
|
|
574
620
|
ShopifyAgent,
|
|
621
|
+
MAX_MEMORY_CHARS,
|
|
622
|
+
MAX_HEARTBEAT_CHARS,
|
|
575
623
|
InMemoryStore,
|
|
576
624
|
DEFAULT_SYSTEM_INSTRUCTION
|
|
577
625
|
};
|
package/dist/memory.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** Result of loadWithStatus() — distinguishes empty history from a storage error. */
|
|
2
|
+
export interface LoadResult {
|
|
3
|
+
history: any[];
|
|
4
|
+
error: boolean;
|
|
5
|
+
}
|
|
1
6
|
/**
|
|
2
7
|
* Interface for persisting conversation history across sessions.
|
|
3
8
|
* Implement this interface to plug in any storage backend (Redis, SQLite, etc.).
|
|
@@ -12,6 +17,8 @@ export interface MemoryStore {
|
|
|
12
17
|
load(sessionId: string): Promise<any[]>;
|
|
13
18
|
/** Clear conversation history for a session. */
|
|
14
19
|
clear(sessionId: string): Promise<void>;
|
|
20
|
+
/** Optional: load with error status for callers that need to distinguish empty from failed. */
|
|
21
|
+
loadWithStatus?(sessionId: string): Promise<LoadResult>;
|
|
15
22
|
}
|
|
16
23
|
/**
|
|
17
24
|
* Simple in-memory implementation of MemoryStore.
|
|
@@ -22,5 +29,6 @@ export declare class InMemoryStore implements MemoryStore {
|
|
|
22
29
|
save(sessionId: string, history: any[]): Promise<void>;
|
|
23
30
|
load(sessionId: string): Promise<any[]>;
|
|
24
31
|
clear(sessionId: string): Promise<void>;
|
|
32
|
+
loadWithStatus(sessionId: string): Promise<LoadResult>;
|
|
25
33
|
}
|
|
26
34
|
//# sourceMappingURL=memory.d.ts.map
|
package/dist/memory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD,iFAAiF;IACjF,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAExC,gDAAgD;IAChD,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD,iFAAiF;IACjF,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAExC,gDAAgD;IAChD,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC,+FAA+F;IAC/F,cAAc,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACzD;AAED;;;GAGG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,KAAK,CAA4B;IAEnC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAKvC,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAG7D"}
|
package/dist/memory.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Max characters allowed for heartbeat content to prevent context bloat. */
|
|
2
|
+
export declare const MAX_HEARTBEAT_CHARS = 2000;
|
|
3
|
+
/** Max characters allowed for memory content to prevent context bloat. */
|
|
4
|
+
export declare const MAX_MEMORY_CHARS = 4000;
|
|
5
|
+
/** Clamp content to a max character count, appending a truncation marker. */
|
|
6
|
+
export declare function clampContent(content: string, maxChars: number): string;
|
|
7
|
+
//# sourceMappingURL=prompt-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-utils.d.ts","sourceRoot":"","sources":["../src/prompt-utils.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAItE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawpify/skills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Shopify Agent SDK — query and manage Shopify stores via GraphQL Admin API with AI agents and MCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -68,9 +68,6 @@
|
|
|
68
68
|
"model-context-protocol"
|
|
69
69
|
],
|
|
70
70
|
"license": "MIT",
|
|
71
|
-
"publishConfig": {
|
|
72
|
-
"access": "public"
|
|
73
|
-
},
|
|
74
71
|
"repository": {
|
|
75
72
|
"type": "git",
|
|
76
73
|
"url": "git+https://github.com/clawpify/skills.git"
|
package/src/agent.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, test, expect, mock } from "bun:test";
|
|
2
2
|
import { ShopifyAgent, DEFAULT_SYSTEM_INSTRUCTION } from "./agent";
|
|
3
|
-
import type { AgentPlugin, AgentHooks, ModelPricing } from "./agent";
|
|
3
|
+
import type { AgentPlugin, AgentHooks, AgentContextConfig, ModelPricing } from "./agent";
|
|
4
4
|
import { ShopifyClient } from "./shopify";
|
|
5
5
|
import { InMemoryStore } from "./memory";
|
|
6
6
|
import type { MemoryStore } from "./memory";
|
|
7
|
+
import { clampContent, MAX_HEARTBEAT_CHARS, MAX_MEMORY_CHARS } from "./prompt-utils";
|
|
7
8
|
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
10
|
// Helpers — build realistic Anthropic response shapes
|
|
@@ -107,6 +108,7 @@ function createAgent(
|
|
|
107
108
|
hooks?: AgentHooks;
|
|
108
109
|
pricing?: ModelPricing;
|
|
109
110
|
systemInstruction?: string;
|
|
111
|
+
context?: AgentContextConfig;
|
|
110
112
|
thinking?: { budgetTokens: number };
|
|
111
113
|
maxIterations?: number;
|
|
112
114
|
memory?: MemoryStore;
|
|
@@ -924,4 +926,133 @@ describe("InMemoryStore", () => {
|
|
|
924
926
|
const loaded = await store.load("test");
|
|
925
927
|
expect(loaded[0].content).toBe("Original");
|
|
926
928
|
});
|
|
929
|
+
|
|
930
|
+
test("loadWithStatus returns history and error: false", async () => {
|
|
931
|
+
const store = new InMemoryStore();
|
|
932
|
+
const history = [
|
|
933
|
+
{ role: "user", content: "Hello" },
|
|
934
|
+
{ role: "assistant", content: "Hi!" },
|
|
935
|
+
];
|
|
936
|
+
|
|
937
|
+
await store.save("test", history);
|
|
938
|
+
const result = await store.loadWithStatus("test");
|
|
939
|
+
|
|
940
|
+
expect(result.error).toBe(false);
|
|
941
|
+
expect(result.history).toEqual(history);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
test("loadWithStatus returns empty history for unknown session", async () => {
|
|
945
|
+
const store = new InMemoryStore();
|
|
946
|
+
const result = await store.loadWithStatus("nonexistent");
|
|
947
|
+
|
|
948
|
+
expect(result).toEqual({ history: [], error: false });
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
// ==========================================================================
|
|
953
|
+
// 11. clampContent & prompt constants
|
|
954
|
+
// ==========================================================================
|
|
955
|
+
|
|
956
|
+
describe("clampContent", () => {
|
|
957
|
+
test("returns trimmed content when under limit", () => {
|
|
958
|
+
expect(clampContent(" hello world ", 100)).toBe("hello world");
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
test("truncates content that exceeds maxChars", () => {
|
|
962
|
+
const long = "a".repeat(3000);
|
|
963
|
+
const result = clampContent(long, 2000);
|
|
964
|
+
expect(result).toContain("...[truncated]");
|
|
965
|
+
// The content portion should be at most maxChars
|
|
966
|
+
expect(result.replace("\n...[truncated]", "").length).toBeLessThanOrEqual(
|
|
967
|
+
2000
|
|
968
|
+
);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
test("returns exact content at the boundary", () => {
|
|
972
|
+
const exact = "b".repeat(2000);
|
|
973
|
+
expect(clampContent(exact, 2000)).toBe(exact);
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
test("constants have expected values", () => {
|
|
977
|
+
expect(MAX_HEARTBEAT_CHARS).toBe(2000);
|
|
978
|
+
expect(MAX_MEMORY_CHARS).toBe(4000);
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
// ==========================================================================
|
|
983
|
+
// 12. AgentContextConfig
|
|
984
|
+
// ==========================================================================
|
|
985
|
+
|
|
986
|
+
describe("AgentContextConfig", () => {
|
|
987
|
+
test("systemInstruction as string still works (backward compat)", () => {
|
|
988
|
+
const agent = createAgent({
|
|
989
|
+
systemInstruction: "Custom instruction",
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
const instruction = (agent as any).systemInstruction;
|
|
993
|
+
expect(instruction).toBe("Custom instruction");
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
test("uses DEFAULT_SYSTEM_INSTRUCTION when neither systemInstruction nor context provided", () => {
|
|
997
|
+
const agent = createAgent();
|
|
998
|
+
|
|
999
|
+
const instruction = (agent as any).systemInstruction;
|
|
1000
|
+
expect(instruction).toBe(DEFAULT_SYSTEM_INSTRUCTION);
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
test("assembles prompt from context when systemInstruction is not provided", () => {
|
|
1004
|
+
const agent = createAgent({
|
|
1005
|
+
context: {
|
|
1006
|
+
heartbeatContent: "Check sales report",
|
|
1007
|
+
memoryContent: "This store sells sneakers",
|
|
1008
|
+
},
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
const instruction: string = (agent as any).systemInstruction;
|
|
1012
|
+
|
|
1013
|
+
// Should start with the default base
|
|
1014
|
+
expect(instruction).toContain(DEFAULT_SYSTEM_INSTRUCTION);
|
|
1015
|
+
// Should include heartbeat block
|
|
1016
|
+
expect(instruction).toContain("NEW conversation");
|
|
1017
|
+
expect(instruction).toContain("Check sales report");
|
|
1018
|
+
// Should include memory block
|
|
1019
|
+
expect(instruction).toContain("Things to remember");
|
|
1020
|
+
expect(instruction).toContain("This store sells sneakers");
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
test("systemInstruction takes precedence over context", () => {
|
|
1024
|
+
const agent = createAgent({
|
|
1025
|
+
systemInstruction: "Override wins",
|
|
1026
|
+
context: {
|
|
1027
|
+
heartbeatContent: "Should not appear",
|
|
1028
|
+
memoryContent: "Should not appear either",
|
|
1029
|
+
},
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
const instruction: string = (agent as any).systemInstruction;
|
|
1033
|
+
expect(instruction).toBe("Override wins");
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
test("context with baseInstruction overrides the default", () => {
|
|
1037
|
+
const agent = createAgent({
|
|
1038
|
+
context: {
|
|
1039
|
+
baseInstruction: "You are a custom assistant.",
|
|
1040
|
+
memoryContent: "Sells hats",
|
|
1041
|
+
},
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
const instruction: string = (agent as any).systemInstruction;
|
|
1045
|
+
expect(instruction).toContain("You are a custom assistant.");
|
|
1046
|
+
expect(instruction).toContain("Sells hats");
|
|
1047
|
+
expect(instruction).not.toContain(DEFAULT_SYSTEM_INSTRUCTION);
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
test("context with no dynamic content uses base instruction only", () => {
|
|
1051
|
+
const agent = createAgent({
|
|
1052
|
+
context: {},
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
const instruction: string = (agent as any).systemInstruction;
|
|
1056
|
+
expect(instruction).toBe(DEFAULT_SYSTEM_INSTRUCTION);
|
|
1057
|
+
});
|
|
927
1058
|
});
|
package/src/agent.ts
CHANGED
|
@@ -2,6 +2,11 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
2
2
|
import { ShopifyClient } from "./shopify";
|
|
3
3
|
import type { MemoryStore } from "./memory";
|
|
4
4
|
import { loadSkillReference } from "./skills";
|
|
5
|
+
import {
|
|
6
|
+
clampContent,
|
|
7
|
+
MAX_HEARTBEAT_CHARS,
|
|
8
|
+
MAX_MEMORY_CHARS,
|
|
9
|
+
} from "./prompt-utils";
|
|
5
10
|
|
|
6
11
|
// ---------------------------------------------------------------------------
|
|
7
12
|
// Types
|
|
@@ -75,13 +80,26 @@ export interface ThinkingConfig {
|
|
|
75
80
|
budgetTokens: number;
|
|
76
81
|
}
|
|
77
82
|
|
|
83
|
+
/** Structured context for prompt assembly. Used when systemInstruction is not provided. */
|
|
84
|
+
export interface AgentContextConfig {
|
|
85
|
+
/** Heartbeat content (only for new sessions). Injected at prompt start. */
|
|
86
|
+
heartbeatContent?: string;
|
|
87
|
+
/** Long-term memory content. Always included. */
|
|
88
|
+
memoryContent?: string;
|
|
89
|
+
/** Base system instruction override. Falls back to DEFAULT_SYSTEM_INSTRUCTION. */
|
|
90
|
+
baseInstruction?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
78
93
|
/** Full configuration accepted by the ShopifyAgent constructor. */
|
|
79
94
|
export interface AgentConfig {
|
|
80
95
|
shopify: ShopifyClient;
|
|
81
96
|
skillContent: string;
|
|
82
97
|
model?: string;
|
|
83
|
-
/** Override the default system instruction sent to the model. */
|
|
98
|
+
/** Override the default system instruction sent to the model (raw string). */
|
|
84
99
|
systemInstruction?: string;
|
|
100
|
+
/** Structured context assembled into a system instruction internally.
|
|
101
|
+
* Ignored when systemInstruction is provided. */
|
|
102
|
+
context?: AgentContextConfig;
|
|
85
103
|
/** Override default Claude Sonnet pricing. */
|
|
86
104
|
pricing?: ModelPricing;
|
|
87
105
|
/** Register plugins at construction time. */
|
|
@@ -243,6 +261,32 @@ function extractContent(response: Anthropic.Message): {
|
|
|
243
261
|
return { text, thinking };
|
|
244
262
|
}
|
|
245
263
|
|
|
264
|
+
/** Assemble a system instruction from structured AgentContextConfig. */
|
|
265
|
+
function buildSystemInstruction(ctx: AgentContextConfig): string {
|
|
266
|
+
const base = ctx.baseInstruction ?? DEFAULT_SYSTEM_INSTRUCTION;
|
|
267
|
+
|
|
268
|
+
let heartbeatBlock = "";
|
|
269
|
+
if (ctx.heartbeatContent?.trim()) {
|
|
270
|
+
heartbeatBlock =
|
|
271
|
+
"\n\nThis is a NEW conversation (no recent chat history). " +
|
|
272
|
+
"Follow these instructions first, then respond to the merchant's message. " +
|
|
273
|
+
"Keep the summary brief (1-2 sentences), then address what they asked.\n\n---\n" +
|
|
274
|
+
clampContent(ctx.heartbeatContent, MAX_HEARTBEAT_CHARS) +
|
|
275
|
+
"\n---";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let memoryBlock = "";
|
|
279
|
+
if (ctx.memoryContent?.trim()) {
|
|
280
|
+
memoryBlock =
|
|
281
|
+
"\n\nThings to remember about this merchant/store " +
|
|
282
|
+
"(use this context in your responses):\n---\n" +
|
|
283
|
+
clampContent(ctx.memoryContent, MAX_MEMORY_CHARS) +
|
|
284
|
+
"\n---";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return base + heartbeatBlock + memoryBlock;
|
|
288
|
+
}
|
|
289
|
+
|
|
246
290
|
// ---------------------------------------------------------------------------
|
|
247
291
|
// Agent
|
|
248
292
|
// ---------------------------------------------------------------------------
|
|
@@ -264,8 +308,15 @@ export class ShopifyAgent {
|
|
|
264
308
|
this.anthropic = new Anthropic();
|
|
265
309
|
this.shopify = config.shopify;
|
|
266
310
|
this.skillContent = config.skillContent;
|
|
267
|
-
|
|
268
|
-
|
|
311
|
+
|
|
312
|
+
if (config.systemInstruction != null) {
|
|
313
|
+
this.systemInstruction = config.systemInstruction;
|
|
314
|
+
} else if (config.context) {
|
|
315
|
+
this.systemInstruction = buildSystemInstruction(config.context);
|
|
316
|
+
} else {
|
|
317
|
+
this.systemInstruction = DEFAULT_SYSTEM_INSTRUCTION;
|
|
318
|
+
}
|
|
319
|
+
|
|
269
320
|
this.model = config.model ?? "claude-sonnet-4-5";
|
|
270
321
|
this.pricing = config.pricing ?? DEFAULT_PRICING;
|
|
271
322
|
this.hooks = config.hooks ?? {};
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export {
|
|
|
49
49
|
export { ShopifyAgent, DEFAULT_SYSTEM_INSTRUCTION } from "./agent";
|
|
50
50
|
export type {
|
|
51
51
|
AgentConfig,
|
|
52
|
+
AgentContextConfig,
|
|
52
53
|
AgentHooks,
|
|
53
54
|
AgentPlugin,
|
|
54
55
|
ChatResult,
|
|
@@ -60,7 +61,10 @@ export type {
|
|
|
60
61
|
|
|
61
62
|
// Memory
|
|
62
63
|
export { InMemoryStore } from "./memory";
|
|
63
|
-
export type { MemoryStore } from "./memory";
|
|
64
|
+
export type { MemoryStore, LoadResult } from "./memory";
|
|
65
|
+
|
|
66
|
+
// Prompt utilities
|
|
67
|
+
export { clampContent, MAX_HEARTBEAT_CHARS, MAX_MEMORY_CHARS } from "./prompt-utils";
|
|
64
68
|
|
|
65
69
|
// Skills loader
|
|
66
70
|
export {
|
package/src/memory.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** Result of loadWithStatus() — distinguishes empty history from a storage error. */
|
|
2
|
+
export interface LoadResult {
|
|
3
|
+
history: any[];
|
|
4
|
+
error: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* Interface for persisting conversation history across sessions.
|
|
3
9
|
* Implement this interface to plug in any storage backend (Redis, SQLite, etc.).
|
|
@@ -14,6 +20,9 @@ export interface MemoryStore {
|
|
|
14
20
|
|
|
15
21
|
/** Clear conversation history for a session. */
|
|
16
22
|
clear(sessionId: string): Promise<void>;
|
|
23
|
+
|
|
24
|
+
/** Optional: load with error status for callers that need to distinguish empty from failed. */
|
|
25
|
+
loadWithStatus?(sessionId: string): Promise<LoadResult>;
|
|
17
26
|
}
|
|
18
27
|
|
|
19
28
|
/**
|
|
@@ -35,4 +44,8 @@ export class InMemoryStore implements MemoryStore {
|
|
|
35
44
|
async clear(sessionId: string): Promise<void> {
|
|
36
45
|
this.store.delete(sessionId);
|
|
37
46
|
}
|
|
47
|
+
|
|
48
|
+
async loadWithStatus(sessionId: string): Promise<LoadResult> {
|
|
49
|
+
return { history: await this.load(sessionId), error: false };
|
|
50
|
+
}
|
|
38
51
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Max characters allowed for heartbeat content to prevent context bloat. */
|
|
2
|
+
export const MAX_HEARTBEAT_CHARS = 2000;
|
|
3
|
+
|
|
4
|
+
/** Max characters allowed for memory content to prevent context bloat. */
|
|
5
|
+
export const MAX_MEMORY_CHARS = 4000;
|
|
6
|
+
|
|
7
|
+
/** Clamp content to a max character count, appending a truncation marker. */
|
|
8
|
+
export function clampContent(content: string, maxChars: number): string {
|
|
9
|
+
const trimmed = content.trim();
|
|
10
|
+
if (trimmed.length <= maxChars) return trimmed;
|
|
11
|
+
return trimmed.slice(0, maxChars).trimEnd() + "\n...[truncated]";
|
|
12
|
+
}
|