Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"dev": "OPENWORK_DEV_MODE=1 bun src/cli.ts",
"test": "bun test",
"test:artifacts": "bun test src/artifact-files.e2e.test.ts src/workspace-init.test.ts src/server.normalizeWorkspaceRelativePath.test.ts",
"build": "tsc -p tsconfig.json && bun build src/opencode-plugins/openwork-extensions-preview.ts src/opencode-plugins/openwork-capabilities-knowledge.ts src/opencode-plugins/openwork-anthropic-adaptive-thinking.ts src/opencode-plugins/openwork-anthropic-tool-schema.ts --outdir dist/opencode-plugins --target node --format esm",
"build": "tsc -p tsconfig.json && bun build src/opencode-plugins/openwork-extensions-preview.ts src/opencode-plugins/openwork-capabilities-knowledge.ts src/opencode-plugins/openwork-anthropic-adaptive-thinking.ts src/opencode-plugins/openwork-anthropic-tool-schema.ts src/opencode-plugins/openwork-system-prompt-normalizer.ts --outdir dist/opencode-plugins --target node --format esm",
"build:bin": "bun build --compile src/cli.ts --outfile dist/bin/openwork-server",
"build:bin:all": "bun ./script/build.ts --outdir dist/bin --target bun-darwin-arm64 --target bun-darwin-x64 --target bun-linux-x64 --target bun-linux-arm64 --target bun-windows-x64",
"start": "bun dist/cli.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ import { resolve } from "node:path";
import { OpenWorkCapabilitiesKnowledge } from "./openwork-capabilities-knowledge.js";

describe("OpenWork capabilities knowledge plugin", () => {
test("adds capabilities knowledge to the system prompt", async () => {
const plugin = await OpenWorkCapabilitiesKnowledge();
const output = {
system: [
"You are OpenWork.",
"",
],
};

await plugin["experimental.chat.system.transform"]({}, output);

expect(output.system).toHaveLength(3);
expect(output.system[0]).toBe("You are OpenWork.");
expect(output.system[2]).toContain("You are running inside OpenWork");
expect(output.system[2]).toContain("OpenWork product questions");
});

test("retrieves Slack connection guidance from bundled docs", async () => {
process.env.OPENWORK_DOCS_DIR = resolve(import.meta.dir, "../../../../packages/docs");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@ function excerpt(content: string, query: string): string {
export const OpenWorkCapabilitiesKnowledge = async () => ({
"experimental.chat.system.transform": async (_input: unknown, output: { system: string[] }) => {
output.system.push(OPENWORK_CAPABILITIES_KNOWLEDGE);

// Strict OpenAI-compatible proxies reject multiple system messages.
// Keep the knowledge, but send it as one provider-safe system prompt.
// Mutate the array in-place to ensure the framework sees the change
const merged = output.system.filter((entry) => entry.trim().length > 0).join("\n\n");
output.system.length = 0;
if (merged) output.system.push(merged);
},
tool: {
openwork_docs_search: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, test } from "bun:test";
import { OpenWorkExtensionsPreview } from "./openwork-extensions-preview.js";

describe("OpenWork extensions preview plugin", () => {
test("adds extension guidance to the system prompt", async () => {
const plugin = await OpenWorkExtensionsPreview();
const output = {
system: ["You are a title generator.", ""],
};

await plugin["experimental.chat.system.transform"]({}, output);

expect(output.system).toHaveLength(4);
expect(output.system[0]).toBe("You are a title generator.");
expect(output.system[2]).toContain("check OpenWork extensions");
expect(output.system[3]).toContain("openwork_ui_execute_action");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ export const OpenWorkExtensionsPreview = async () => ({
"experimental.chat.system.transform": async (_input: unknown, output: { system: string[] }) => {
output.system.push(OPENWORK_EXTENSION_DISCOVERY_INSTRUCTION);
output.system.push(OPENWORK_UI_CONTROL_INSTRUCTION);

// Strict OpenAI-compatible proxies reject multiple system messages.
// Keep the guidance, but send it as one provider-safe system prompt.
// Mutate the array in-place to ensure the framework sees the change
const merged = output.system.filter((entry) => entry.trim().length > 0).join("\n\n");
output.system.length = 0;
if (merged) output.system.push(merged);
},
tool: {
openwork_extension_list_actions: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, test } from "bun:test";
import { OpenWorkCapabilitiesKnowledge } from "./openwork-capabilities-knowledge.js";
import { OpenWorkExtensionsPreview } from "./openwork-extensions-preview.js";
import { mergeSystemPromptsInPlace, OpenWorkSystemPromptNormalizer } from "./openwork-system-prompt-normalizer.js";

describe("OpenWork system prompt normalizer", () => {
test("merges multiple system prompts in place", () => {
const system = [" base prompt ", "", " plugin prompt "];
const original = system;

mergeSystemPromptsInPlace(system);

expect(system).toBe(original);
expect(system).toEqual(["base prompt\n\nplugin prompt"]);
});

test("runs after OpenWork plugin transforms to produce one provider-safe system prompt", async () => {
const capabilities = await OpenWorkCapabilitiesKnowledge();
const extensions = await OpenWorkExtensionsPreview();
const normalizer = await OpenWorkSystemPromptNormalizer();
const output = {
system: ["You are OpenWork.", ""],
};

await capabilities["experimental.chat.system.transform"]({}, output);
await extensions["experimental.chat.system.transform"]({}, output);
expect(output.system.length).toBeGreaterThan(1);

await normalizer["experimental.chat.system.transform"]({}, output);

expect(output.system).toHaveLength(1);
expect(output.system[0]).toContain("You are OpenWork.");
expect(output.system[0]).toContain("You are running inside OpenWork");
expect(output.system[0]).toContain("check OpenWork extensions");
expect(output.system[0]).toContain("openwork_ui_execute_action");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type SystemTransformOutput = {
system: string[];
};

export function mergeSystemPromptsInPlace(system: string[]): void {
const merged = system
.map((entry) => entry.trim())
.filter(Boolean)
.join("\n\n");

// Keep the same array reference for hook callers that hold onto it.
system.length = 0;
if (merged) system.push(merged);
}

export const OpenWorkSystemPromptNormalizer = async () => ({
"experimental.chat.system.transform": async (_input: unknown, output: SystemTransformOutput) => {
mergeSystemPromptsInPlace(output.system);
},
});
1 change: 1 addition & 0 deletions apps/server/src/openwork-extensions-plugin-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ export function openworkPluginPath(name: string, here = dirname(fileURLToPath(im

export const openworkExtensionsPreviewPluginPath = () => openworkPluginPath("openwork-extensions-preview");
export const openworkCapabilitiesKnowledgePluginPath = () => openworkPluginPath("openwork-capabilities-knowledge");
export const openworkSystemPromptNormalizerPluginPath = () => openworkPluginPath("openwork-system-prompt-normalizer");
export const openworkAnthropicAdaptiveThinkingPluginPath = () => openworkPluginPath("openwork-anthropic-adaptive-thinking");
export const openworkAnthropicToolSchemaPluginPath = () => openworkPluginPath("openwork-anthropic-tool-schema");
2 changes: 2 additions & 0 deletions apps/server/src/openwork-runtime-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { randomUUID } from "node:crypto";
import {
openworkExtensionsPreviewPluginPath,
openworkCapabilitiesKnowledgePluginPath,
openworkSystemPromptNormalizerPluginPath,
openworkAnthropicAdaptiveThinkingPluginPath,
openworkAnthropicToolSchemaPluginPath,
} from "./openwork-extensions-plugin-path.js";
Expand Down Expand Up @@ -89,6 +90,7 @@ export async function buildOpenworkRuntimeConfigObject(
openworkAnthropicAdaptiveThinkingPluginPath(),
openworkAnthropicToolSchemaPluginPath(),
...runtimePluginList(runtimeConfig),
openworkSystemPromptNormalizerPluginPath(),
],
...(disabledProviders.length ? { disabled_providers: disabledProviders } : {}),
mcp: runtimeMcpMap(runtimeConfig),
Expand Down