@thinkwell/acp 0.5.4 → 0.5.6

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/session.js CHANGED
@@ -1,165 +1,119 @@
1
- /**
2
- * Active session with an agent
3
- */
4
1
  export class ActiveSession {
5
- sessionId;
6
- _connection;
7
- _mcpHandler;
8
- _pendingUpdates = [];
9
- _updateResolvers = [];
10
- _closed = false;
11
- constructor(sessionId, connection, mcpHandler) {
12
- this.sessionId = sessionId;
13
- this._connection = connection;
14
- this._mcpHandler = mcpHandler;
15
- this._mcpHandler.setSessionId(sessionId);
16
- }
17
- /**
18
- * Send a prompt to the agent
19
- */
20
- async sendPrompt(content) {
21
- const response = await this._connection.sendPrompt(this.sessionId, content);
22
- // Push a stop update when the prompt completes
23
- if (response.stopReason) {
24
- this.pushUpdate({ type: "stop", reason: response.stopReason });
25
- }
26
- }
27
- /**
28
- * Push an update received from the agent
29
- */
30
- pushUpdate(update) {
31
- if (this._updateResolvers.length > 0) {
32
- const resolver = this._updateResolvers.shift();
33
- resolver(update);
34
- }
35
- else {
36
- this._pendingUpdates.push(update);
37
- }
38
- }
39
- /**
40
- * Read the next update from the agent
41
- */
42
- async readUpdate() {
43
- if (this._pendingUpdates.length > 0) {
44
- return this._pendingUpdates.shift();
45
- }
46
- if (this._closed) {
47
- return { type: "stop", reason: "session_closed" };
48
- }
49
- return new Promise((resolve) => {
50
- this._updateResolvers.push(resolve);
51
- });
52
- }
53
- /**
54
- * Read all updates until completion, returning concatenated text
55
- */
56
- async readToString() {
57
- const parts = [];
58
- while (true) {
59
- const update = await this.readUpdate();
60
- if (update.type === "text") {
61
- parts.push(update.content);
62
- }
63
- else if (update.type === "stop") {
64
- break;
65
- }
66
- // tool_use updates are handled by the connection layer
67
- }
68
- return parts.join("");
69
- }
70
- /**
71
- * Mark the session as closed
72
- */
73
- close() {
74
- this._closed = true;
75
- // Resolve any pending readers
76
- for (const resolver of this._updateResolvers) {
77
- resolver({ type: "stop", reason: "session_closed" });
78
- }
79
- this._updateResolvers = [];
2
+ sessionId;
3
+ _connection;
4
+ _mcpHandler;
5
+ _pendingUpdates = [];
6
+ _updateResolvers = [];
7
+ _closed = !1;
8
+ constructor(sessionId, connection, mcpHandler) {
9
+ this.sessionId = sessionId, this._connection = connection, this._mcpHandler = mcpHandler, this._mcpHandler.setSessionId(sessionId);
10
+ }
11
+ /**
12
+ * Send a prompt to the agent
13
+ */
14
+ async sendPrompt(content) {
15
+ const response = await this._connection.sendPrompt(this.sessionId, content);
16
+ response.stopReason && this.pushUpdate({ type: "stop", reason: response.stopReason });
17
+ }
18
+ /**
19
+ * Push an update received from the agent
20
+ */
21
+ pushUpdate(update) {
22
+ this._updateResolvers.length > 0 ? this._updateResolvers.shift()(update) : this._pendingUpdates.push(update);
23
+ }
24
+ /**
25
+ * Read the next update from the agent
26
+ */
27
+ async readUpdate() {
28
+ return this._pendingUpdates.length > 0 ? this._pendingUpdates.shift() : this._closed ? { type: "stop", reason: "session_closed" } : new Promise((resolve) => {
29
+ this._updateResolvers.push(resolve);
30
+ });
31
+ }
32
+ /**
33
+ * Read all updates until completion, returning concatenated text
34
+ */
35
+ async readToString() {
36
+ const parts = [];
37
+ for (; ; ) {
38
+ const update = await this.readUpdate();
39
+ if (update.type === "text")
40
+ parts.push(update.content);
41
+ else if (update.type === "stop")
42
+ break;
80
43
  }
44
+ return parts.join("");
45
+ }
46
+ /**
47
+ * Mark the session as closed
48
+ */
49
+ close() {
50
+ this._closed = !0;
51
+ for (const resolver of this._updateResolvers)
52
+ resolver({ type: "stop", reason: "session_closed" });
53
+ this._updateResolvers = [];
54
+ }
81
55
  }
82
- /**
83
- * Builder for creating ACP sessions with MCP servers
84
- */
85
56
  export class SessionBuilder {
86
- _connection;
87
- _mcpHandler;
88
- _mcpServers = [];
89
- _cwd;
90
- _systemPrompt;
91
- _mcpReadyOptions = {};
92
- constructor(connection, mcpHandler) {
93
- this._connection = connection;
94
- this._mcpHandler = mcpHandler;
95
- }
96
- /**
97
- * Attach an MCP server to this session
98
- */
99
- withMcpServer(server) {
100
- this._mcpServers.push(server);
101
- this._mcpHandler.register(server);
102
- return this;
103
- }
104
- /**
105
- * Set the working directory for the session
106
- */
107
- cwd(path) {
108
- this._cwd = path;
109
- return this;
110
- }
111
- /**
112
- * Set the system prompt for the session
113
- */
114
- systemPrompt(prompt) {
115
- this._systemPrompt = prompt;
116
- return this;
117
- }
118
- /**
119
- * Configure waiting for MCP tools discovery.
120
- *
121
- * By default, when MCP servers are attached, the session will wait for
122
- * the agent to call tools/list before invoking the callback. This prevents
123
- * a race condition where the prompt is sent before the agent knows about
124
- * available tools.
125
- *
126
- * @param options - Options for MCP readiness waiting
127
- */
128
- waitForMcpReady(options = {}) {
129
- this._mcpReadyOptions = options;
130
- return this;
131
- }
132
- /**
133
- * Start the session and run a callback
134
- */
135
- async run(callback) {
136
- // Create the session
137
- const sessionId = await this._connection.createSession({
138
- cwd: this._cwd,
139
- systemPrompt: this._systemPrompt,
140
- mcpServers: this._mcpServers.map((s) => s.toSessionConfig()),
141
- });
142
- const session = new ActiveSession(sessionId, this._connection, this._mcpHandler);
143
- // Set up the message pump for this session
144
- this._connection.setSessionHandler(sessionId, session);
145
- try {
146
- // Wait for MCP tools discovery if we have MCP servers attached
147
- const shouldWait = this._mcpServers.length > 0 &&
148
- (this._mcpReadyOptions.enabled !== false);
149
- if (shouldWait) {
150
- const timeout = this._mcpReadyOptions.timeout ?? 2000;
151
- await this._mcpHandler.waitForToolsDiscovery(sessionId, timeout);
152
- }
153
- return await callback(session);
154
- }
155
- finally {
156
- session.close();
157
- this._connection.removeSessionHandler(sessionId);
158
- // Unregister MCP servers
159
- for (const server of this._mcpServers) {
160
- this._mcpHandler.unregister(server);
161
- }
162
- }
57
+ _connection;
58
+ _mcpHandler;
59
+ _mcpServers = [];
60
+ _cwd;
61
+ _systemPrompt;
62
+ _mcpReadyOptions = {};
63
+ constructor(connection, mcpHandler) {
64
+ this._connection = connection, this._mcpHandler = mcpHandler;
65
+ }
66
+ /**
67
+ * Attach an MCP server to this session
68
+ */
69
+ withMcpServer(server) {
70
+ return this._mcpServers.push(server), this._mcpHandler.register(server), this;
71
+ }
72
+ /**
73
+ * Set the working directory for the session
74
+ */
75
+ cwd(path) {
76
+ return this._cwd = path, this;
77
+ }
78
+ /**
79
+ * Set the system prompt for the session
80
+ */
81
+ systemPrompt(prompt) {
82
+ return this._systemPrompt = prompt, this;
83
+ }
84
+ /**
85
+ * Configure waiting for MCP tools discovery.
86
+ *
87
+ * By default, when MCP servers are attached, the session will wait for
88
+ * the agent to call tools/list before invoking the callback. This prevents
89
+ * a race condition where the prompt is sent before the agent knows about
90
+ * available tools.
91
+ *
92
+ * @param options - Options for MCP readiness waiting
93
+ */
94
+ waitForMcpReady(options = {}) {
95
+ return this._mcpReadyOptions = options, this;
96
+ }
97
+ /**
98
+ * Start the session and run a callback
99
+ */
100
+ async run(callback) {
101
+ const sessionId = await this._connection.createSession({
102
+ cwd: this._cwd,
103
+ systemPrompt: this._systemPrompt,
104
+ mcpServers: this._mcpServers.map((s) => s.toSessionConfig())
105
+ }), session = new ActiveSession(sessionId, this._connection, this._mcpHandler);
106
+ this._connection.setSessionHandler(sessionId, session);
107
+ try {
108
+ if (this._mcpServers.length > 0 && this._mcpReadyOptions.enabled !== !1) {
109
+ const timeout = this._mcpReadyOptions.timeout ?? 2e3;
110
+ await this._mcpHandler.waitForToolsDiscovery(sessionId, timeout);
111
+ }
112
+ return await callback(session);
113
+ } finally {
114
+ session.close(), this._connection.removeSessionHandler(sessionId);
115
+ for (const server of this._mcpServers)
116
+ this._mcpHandler.unregister(server);
163
117
  }
118
+ }
164
119
  }
165
- //# sourceMappingURL=session.js.map
@@ -1,96 +1,61 @@
1
- /**
2
- * Skill MCP server builder.
3
- *
4
- * Creates an MCP server that exposes three tools for skill interaction:
5
- * - activate_skill: returns a skill's instruction body
6
- * - call_skill_tool: dispatches to a virtual skill's tool handler
7
- * - read_skill_file: reads a file from a stored skill's basePath
8
- *
9
- * These tools are registered as standard MCP tools but are intended to be
10
- * hidden from the prompt (via Plan's defineTool pattern).
11
- */
12
1
  import { readFile } from "node:fs/promises";
13
2
  import { resolve, relative, isAbsolute } from "node:path";
14
3
  import { mcpServer } from "./mcp-server.js";
15
- /** Type guard for StoredSkill (has basePath). */
16
4
  function isStoredSkill(skill) {
17
- return "basePath" in skill && typeof skill.basePath === "string";
5
+ return "basePath" in skill && typeof skill.basePath == "string";
18
6
  }
19
- /**
20
- * Build an MCP server that provides skill tools.
21
- *
22
- * @param skills - resolved skills (virtual or stored) to make available
23
- * @returns an McpServer with activate_skill, call_skill_tool, and read_skill_file tools
24
- */
25
7
  export function createSkillServer(skills) {
26
- const skillsByName = new Map();
27
- for (const skill of skills) {
28
- skillsByName.set(skill.name, skill);
29
- }
30
- return mcpServer("skills")
31
- .tool("activate_skill", "Activate a skill by name, returning its full instructions.", {
32
- type: "object",
33
- properties: {
34
- skill_name: { type: "string", description: "The name of the skill to activate" },
35
- },
36
- required: ["skill_name"],
37
- }, { type: "string" }, async (input) => {
38
- const skill = skillsByName.get(input.skill_name);
39
- if (!skill) {
40
- throw new Error(`Unknown skill: "${input.skill_name}"`);
41
- }
42
- return skill.body;
43
- })
44
- .tool("call_skill_tool", "Call a tool provided by a skill.", {
45
- type: "object",
46
- properties: {
47
- skill_name: { type: "string", description: "The name of the skill that provides the tool" },
48
- tool_name: { type: "string", description: "The name of the tool to call" },
49
- input: { description: "Input to pass to the tool handler" },
50
- },
51
- required: ["skill_name", "tool_name"],
52
- }, {}, async (input) => {
53
- const skill = skillsByName.get(input.skill_name);
54
- if (!skill) {
55
- throw new Error(`Unknown skill: "${input.skill_name}"`);
56
- }
57
- const tools = skill.tools;
58
- if (!tools || tools.length === 0) {
59
- throw new Error(`Skill "${input.skill_name}" has no tools`);
60
- }
61
- const tool = tools.find((t) => t.name === input.tool_name);
62
- if (!tool) {
63
- throw new Error(`Unknown tool "${input.tool_name}" for skill "${input.skill_name}"`);
64
- }
65
- return tool.handler(input.input);
66
- })
67
- .tool("read_skill_file", "Read a file from a stored skill's directory.", {
68
- type: "object",
69
- properties: {
70
- skill_name: { type: "string", description: "The name of the skill" },
71
- path: { type: "string", description: "Relative path to the file within the skill directory" },
72
- },
73
- required: ["skill_name", "path"],
74
- }, { type: "string" }, async (input) => {
75
- const skill = skillsByName.get(input.skill_name);
76
- if (!skill) {
77
- throw new Error(`Unknown skill: "${input.skill_name}"`);
78
- }
79
- if (!isStoredSkill(skill)) {
80
- throw new Error(`Skill "${input.skill_name}" is not a stored skill (no basePath)`);
81
- }
82
- // Reject absolute paths outright
83
- if (isAbsolute(input.path)) {
84
- throw new Error("Path must be relative");
85
- }
86
- const resolved = resolve(skill.basePath, input.path);
87
- // Path traversal check: resolved path must be within basePath
88
- const rel = relative(skill.basePath, resolved);
89
- if (rel.startsWith("..") || isAbsolute(rel)) {
90
- throw new Error("Path traversal is not allowed");
91
- }
92
- return readFile(resolved, "utf-8");
93
- })
94
- .build();
8
+ const skillsByName = /* @__PURE__ */ new Map();
9
+ for (const skill of skills)
10
+ skillsByName.set(skill.name, skill);
11
+ return mcpServer("skills").tool("activate_skill", "Activate a skill by name, returning its full instructions.", {
12
+ type: "object",
13
+ properties: {
14
+ skill_name: { type: "string", description: "The name of the skill to activate" }
15
+ },
16
+ required: ["skill_name"]
17
+ }, { type: "string" }, async (input) => {
18
+ const skill = skillsByName.get(input.skill_name);
19
+ if (!skill)
20
+ throw new Error(`Unknown skill: "${input.skill_name}"`);
21
+ return skill.body;
22
+ }).tool("call_skill_tool", "Call a tool provided by a skill.", {
23
+ type: "object",
24
+ properties: {
25
+ skill_name: { type: "string", description: "The name of the skill that provides the tool" },
26
+ tool_name: { type: "string", description: "The name of the tool to call" },
27
+ input: { description: "Input to pass to the tool handler" }
28
+ },
29
+ required: ["skill_name", "tool_name"]
30
+ }, {}, async (input) => {
31
+ const skill = skillsByName.get(input.skill_name);
32
+ if (!skill)
33
+ throw new Error(`Unknown skill: "${input.skill_name}"`);
34
+ const tools = skill.tools;
35
+ if (!tools || tools.length === 0)
36
+ throw new Error(`Skill "${input.skill_name}" has no tools`);
37
+ const tool = tools.find((t) => t.name === input.tool_name);
38
+ if (!tool)
39
+ throw new Error(`Unknown tool "${input.tool_name}" for skill "${input.skill_name}"`);
40
+ return tool.handler(input.input);
41
+ }).tool("read_skill_file", "Read a file from a stored skill's directory.", {
42
+ type: "object",
43
+ properties: {
44
+ skill_name: { type: "string", description: "The name of the skill" },
45
+ path: { type: "string", description: "Relative path to the file within the skill directory" }
46
+ },
47
+ required: ["skill_name", "path"]
48
+ }, { type: "string" }, async (input) => {
49
+ const skill = skillsByName.get(input.skill_name);
50
+ if (!skill)
51
+ throw new Error(`Unknown skill: "${input.skill_name}"`);
52
+ if (!isStoredSkill(skill))
53
+ throw new Error(`Skill "${input.skill_name}" is not a stored skill (no basePath)`);
54
+ if (isAbsolute(input.path))
55
+ throw new Error("Path must be relative");
56
+ const resolved = resolve(skill.basePath, input.path), rel = relative(skill.basePath, resolved);
57
+ if (rel.startsWith("..") || isAbsolute(rel))
58
+ throw new Error("Path traversal is not allowed");
59
+ return readFile(resolved, "utf-8");
60
+ }).build();
95
61
  }
96
- //# sourceMappingURL=skill-server.js.map
package/dist/skill.js CHANGED
@@ -1,117 +1,51 @@
1
- /**
2
- * Skill types and SKILL.md parser for the Agent Skills standard.
3
- *
4
- * This module defines the core skill types and provides a parser for
5
- * SKILL.md files (YAML frontmatter + Markdown body).
6
- */
7
- /** Validation pattern for skill names: lowercase alphanumeric + hyphens, no leading/trailing/consecutive hyphens. */
8
- const SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9]))*$/;
9
- /** Maximum length for skill names. */
10
- const MAX_NAME_LENGTH = 64;
11
- /** Maximum length for skill descriptions. */
12
- const MAX_DESCRIPTION_LENGTH = 1024;
13
- /**
14
- * Validate a skill name per the Agent Skills spec.
15
- * - 1-64 characters
16
- * - Lowercase alphanumeric + hyphens
17
- * - No leading, trailing, or consecutive hyphens
18
- */
1
+ const SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9]))*$/, MAX_NAME_LENGTH = 64, MAX_DESCRIPTION_LENGTH = 1024;
19
2
  export function validateSkillName(name) {
20
- if (typeof name !== "string" || name.length === 0) {
21
- throw new Error("Skill name is required");
22
- }
23
- if (name.length > MAX_NAME_LENGTH) {
24
- throw new Error(`Skill name must be at most ${MAX_NAME_LENGTH} characters, got ${name.length}`);
25
- }
26
- if (!SKILL_NAME_PATTERN.test(name)) {
27
- throw new Error(`Invalid skill name "${name}": must be lowercase alphanumeric with hyphens, no leading/trailing/consecutive hyphens`);
28
- }
3
+ if (typeof name != "string" || name.length === 0)
4
+ throw new Error("Skill name is required");
5
+ if (name.length > 64)
6
+ throw new Error(`Skill name must be at most 64 characters, got ${name.length}`);
7
+ if (!SKILL_NAME_PATTERN.test(name))
8
+ throw new Error(`Invalid skill name "${name}": must be lowercase alphanumeric with hyphens, no leading/trailing/consecutive hyphens`);
29
9
  }
30
- /**
31
- * Validate a skill description per the Agent Skills spec.
32
- * - 1-1024 characters
33
- * - Non-empty (after trimming)
34
- */
35
10
  export function validateSkillDescription(description) {
36
- if (typeof description !== "string" || description.trim().length === 0) {
37
- throw new Error("Skill description is required");
38
- }
39
- if (description.length > MAX_DESCRIPTION_LENGTH) {
40
- throw new Error(`Skill description must be at most ${MAX_DESCRIPTION_LENGTH} characters, got ${description.length}`);
41
- }
11
+ if (typeof description != "string" || description.trim().length === 0)
12
+ throw new Error("Skill description is required");
13
+ if (description.length > 1024)
14
+ throw new Error(`Skill description must be at most 1024 characters, got ${description.length}`);
42
15
  }
43
- /**
44
- * Parse a SKILL.md file content into a Skill.
45
- *
46
- * Extracts YAML frontmatter (delimited by `---`), validates required fields
47
- * (name, description), and returns the parsed skill with the Markdown body.
48
- *
49
- * Optional frontmatter fields (license, compatibility, metadata) are preserved
50
- * in the returned object but not acted upon.
51
- *
52
- * @throws Error if frontmatter is missing, malformed, or required fields are invalid
53
- */
54
16
  export function parseSkillMd(content) {
55
- const { frontmatter, body } = extractFrontmatter(content);
56
- const fields = parseYamlFrontmatter(frontmatter);
57
- validateSkillName(fields.name);
58
- validateSkillDescription(fields.description);
59
- return {
60
- ...fields,
61
- name: fields.name,
62
- description: fields.description,
63
- body,
64
- };
17
+ const { frontmatter, body } = extractFrontmatter(content), fields = parseYamlFrontmatter(frontmatter);
18
+ return validateSkillName(fields.name), validateSkillDescription(fields.description), {
19
+ ...fields,
20
+ name: fields.name,
21
+ description: fields.description,
22
+ body
23
+ };
65
24
  }
66
- /**
67
- * Extract YAML frontmatter and body from a SKILL.md string.
68
- * Frontmatter is delimited by opening and closing `---` lines.
69
- */
70
25
  function extractFrontmatter(content) {
71
- const trimmed = content.trimStart();
72
- if (!trimmed.startsWith("---")) {
73
- throw new Error("SKILL.md must begin with YAML frontmatter (---)");
74
- }
75
- // Find the closing `---` after the opening one
76
- const endIndex = trimmed.indexOf("\n---", 3);
77
- if (endIndex === -1) {
78
- throw new Error("SKILL.md frontmatter is missing closing ---");
79
- }
80
- const frontmatter = trimmed.slice(3, endIndex).trim();
81
- // Body starts after the closing `---` and its newline
82
- const afterClosing = endIndex + 4; // "\n---".length
83
- // Strip the line ending after the closing --- and any single blank line
84
- const body = trimmed.slice(afterClosing).replace(/^(\r?\n){1,2}/, "");
85
- return { frontmatter, body };
26
+ const trimmed = content.trimStart();
27
+ if (!trimmed.startsWith("---"))
28
+ throw new Error("SKILL.md must begin with YAML frontmatter (---)");
29
+ const endIndex = trimmed.indexOf(`
30
+ ---`, 3);
31
+ if (endIndex === -1)
32
+ throw new Error("SKILL.md frontmatter is missing closing ---");
33
+ const frontmatter = trimmed.slice(3, endIndex).trim(), afterClosing = endIndex + 4, body = trimmed.slice(afterClosing).replace(/^(\r?\n){1,2}/, "");
34
+ return { frontmatter, body };
86
35
  }
87
- /**
88
- * Minimal YAML parser for SKILL.md frontmatter.
89
- *
90
- * Supports only the flat key-value structure needed for skill metadata:
91
- * simple string values (quoted or unquoted). This is intentionally limited --
92
- * we don't need nested objects, arrays, or other YAML features.
93
- */
94
36
  function parseYamlFrontmatter(yaml) {
95
- const result = {};
96
- for (const line of yaml.split("\n")) {
97
- const trimmed = line.trim();
98
- if (trimmed === "" || trimmed.startsWith("#"))
99
- continue;
100
- const colonIndex = trimmed.indexOf(":");
101
- if (colonIndex === -1) {
102
- throw new Error(`Invalid frontmatter line: ${trimmed}`);
103
- }
104
- const key = trimmed.slice(0, colonIndex).trim();
105
- let value = trimmed.slice(colonIndex + 1).trim();
106
- // Strip surrounding quotes (single or double)
107
- if (typeof value === "string") {
108
- if ((value.startsWith('"') && value.endsWith('"')) ||
109
- (value.startsWith("'") && value.endsWith("'"))) {
110
- value = value.slice(1, -1);
111
- }
112
- }
113
- result[key] = value;
114
- }
115
- return result;
37
+ const result = {};
38
+ for (const line of yaml.split(`
39
+ `)) {
40
+ const trimmed = line.trim();
41
+ if (trimmed === "" || trimmed.startsWith("#"))
42
+ continue;
43
+ const colonIndex = trimmed.indexOf(":");
44
+ if (colonIndex === -1)
45
+ throw new Error(`Invalid frontmatter line: ${trimmed}`);
46
+ const key = trimmed.slice(0, colonIndex).trim();
47
+ let value = trimmed.slice(colonIndex + 1).trim();
48
+ typeof value == "string" && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) && (value = value.slice(1, -1)), result[key] = value;
49
+ }
50
+ return result;
116
51
  }
117
- //# sourceMappingURL=skill.js.map
package/dist/types.js CHANGED
@@ -1,8 +1 @@
1
- /**
2
- * Types for ACP protocol messages
3
- *
4
- * This module re-exports shared types from @thinkwell/protocol and
5
- * defines ACP-specific types that are not shared with other packages.
6
- */
7
1
  export {};
8
- //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkwell/acp",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "TypeScript implementation of ACP (Agent Client Protocol) extensions for Thinkwell",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,10 +25,10 @@
25
25
  "author": "David Herman",
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
- "@agentclientprotocol/sdk": "^0.4.0",
28
+ "@agentclientprotocol/sdk": "^0.16.1",
29
29
  "uuid": "^11.0.3",
30
- "@thinkwell/conductor": "0.5.4",
31
- "@thinkwell/protocol": "0.5.4"
30
+ "@thinkwell/conductor": "0.5.6",
31
+ "@thinkwell/protocol": "0.5.6"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^24.10.4",
@@ -37,7 +37,8 @@
37
37
  "typescript": "^5.7.2"
38
38
  },
39
39
  "scripts": {
40
- "build": "tsc",
40
+ "build": "tsc && tsx ../../scripts/strip-features.ts --mode=release",
41
+ "build:debug": "tsc",
41
42
  "clean": "rm -rf dist",
42
43
  "test": "node --test --import tsx src/**/*.test.ts"
43
44
  }