Skip to content

Commit d20cfb3

Browse files
authored
Merge pull request #1761 from constructive-io/feat/db-tools-package
feat(agentic): extract the db tools into @agentic-kit/db-tools
2 parents efb3f19 + 9ec97ba commit d20cfb3

54 files changed

Lines changed: 566 additions & 274 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/run-tests.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
- batch: graphile-unit
116116
packages: 'graphile/graphile-plugin-utils graphile/graphile-realtime-subscriptions graphile/graphile-sql-expression-validator graphile/graphile-upload-plugin'
117117
- batch: agentic
118-
packages: 'agentic/protocol agentic/agentic-kit agentic/agent agentic/harness agentic/chat agentic/cli agentic/pi agentic/react agentic/agentic-server agentic/anthropic agentic/openai agentic/ollama agentic/run-log agentic/metering'
118+
packages: 'agentic/protocol agentic/agentic-kit agentic/agent agentic/harness agentic/chat agentic/cli agentic/db-tools agentic/pi agentic/react agentic/agentic-server agentic/anthropic agentic/openai agentic/ollama agentic/run-log agentic/metering'
119119
- batch: pgpm-unit
120120
packages: 'pgpm/types pgpm/naming-spec pgpm/diff pgpm/import pgpm/slice pgpm/transform'
121121
- batch: pglite

‎agentic/db-tools/README.md‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# @agentic-kit/db-tools
2+
3+
<p align="center" width="100%">
4+
<img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
5+
</p>
6+
7+
<p align="center" width="100%">
8+
<a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
9+
<img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
10+
</a>
11+
<a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
12+
<a href="https://www.npmjs.com/package/@agentic-kit/db-tools"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=agentic%2Fdb-tools%2Fpackage.json"/></a>
13+
</p>
14+
15+
The Constructive typed database tools, as **harness-neutral** [`HarnessTool`](https://www.npmjs.com/package/@agentic-kit/harness)s: zod parameters, `execute(params, { cwd, signal })`, and no coding-agent SDK anywhere in the package. Binding them to a harness is the *adapter's* job — [`@agentic-kit/pi`](https://www.npmjs.com/package/@agentic-kit/pi) does it with `toPiTool()` — so a second, compatible harness registers the same 18 tools without a fork.
16+
17+
```
18+
@agentic-kit/db-tools ──► @agentic-kit/harness (HarnessTool)
19+
▲
20+
├── @agentic-kit/pi toPiTool() ──► pi's ToolDefinition
21+
└── your adapter toXTool() ──► your harness's tool shape
22+
```
23+
24+
```bash
25+
npm install @agentic-kit/db-tools
26+
```
27+
28+
## What's inside
29+
30+
- **18 typed db tools** — `provision_database`, `provision_blueprint`, `describe_schema`, `add_relation`, `delete_table`, `create_field` / `update_field` / `delete_field`, `add_policies`, `add_records`, `manage_entity_types`, `create_api_key`, `run_codegen`, and the template suite (`list` / `create` / `apply` / `update` / `delete`). `constructiveDbTools` is the list, in registration order.
31+
- **Host injection** — credentials, backend endpoints and data-plane tokens come from the host application, not from the package. Call `configureHost()` once at startup; the tools read it lazily per call.
32+
- **Project context** — `resolveProjectContext` / `resolveDataToken` resolve the database a tool acts on from the cwd's `.env` plus the host's session, and `deriveSubdomainEndpoint` derives its per-database endpoints.
33+
- **Provisioning model** — the pinned `node-type-registry` presets, the provision manifest, and overlay resolution (`resolveProvisionModules`).
34+
- **`toolSchema`** — a tool's zod parameters as plain JSON Schema, for adapters whose harness wants JSON Schema rather than zod.
35+
36+
## Usage
37+
38+
```ts
39+
import { configureHost, constructiveDbTools } from '@agentic-kit/db-tools';
40+
41+
configureHost({
42+
account: () => ({ userId, accessToken }),
43+
backendConfig: () => ({ apiEndpoint, modulesEndpoint }),
44+
signInHint: 'Run `agent login` to sign in.'
45+
});
46+
47+
for (const tool of constructiveDbTools) {
48+
myHarness.registerTool(myToolBinding(tool));
49+
}
50+
```
51+
52+
A tool executes against nothing but its params and a context, so it is callable directly — which is also how the tests drive it:
53+
54+
```ts
55+
const result = await describeSchemaTool.execute({ database_name: 'my-app' }, { cwd: process.cwd() });
56+
```
57+
58+
## Related
59+
60+
| Package | Role |
61+
| --- | --- |
62+
| [`@agentic-kit/harness`](https://www.npmjs.com/package/@agentic-kit/harness) | the neutral contracts: `HarnessTool`, `HarnessAdapter`, the run gate |
63+
| [`@agentic-kit/pi`](https://www.npmjs.com/package/@agentic-kit/pi) | the pi adapter: binds these tools and attaches a run's lanes to a pi session |
64+
| [`@agentic-kit/cli`](https://www.npmjs.com/package/@agentic-kit/cli) | the `agent` CLI, which materializes the tools into a pi agent dir |
65+
66+
## License
67+
68+
SEE LICENSE IN LICENSE
File renamed without changes.
File renamed without changes.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { execFileSync } from 'node:child_process';
2+
import path from 'node:path';
3+
4+
const packageRoot = path.join(__dirname, '..');
5+
6+
describe('the db tools package', () => {
7+
it('names no harness in its dependencies', () => {
8+
const pkg = require(path.join(packageRoot, 'package.json')) as {
9+
dependencies?: Record<string, string>;
10+
peerDependencies?: Record<string, string>;
11+
devDependencies?: Record<string, string>;
12+
};
13+
const deps = Object.keys({
14+
...pkg.dependencies,
15+
...pkg.peerDependencies,
16+
...pkg.devDependencies
17+
});
18+
19+
expect(deps.filter((dep) => dep.startsWith('@earendil-works/'))).toEqual([]);
20+
expect(deps).not.toContain('@agentic-kit/pi');
21+
});
22+
23+
it('imports no harness SDK in any source file', () => {
24+
// grep -l exits 1 with no output when nothing matches, which is the pass.
25+
let matches = '';
26+
try {
27+
matches = execFileSync(
28+
'grep',
29+
[
30+
'-rlE',
31+
"(from|require\\()\\s*'(@earendil-works/|@agentic-kit/pi)",
32+
'src'
33+
],
34+
{ cwd: packageRoot, encoding: 'utf8' }
35+
);
36+
} catch (err) {
37+
const status = (err as { status?: number }).status;
38+
if (status !== 1) throw err;
39+
}
40+
41+
expect(matches.trim()).toBe('');
42+
});
43+
});
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { z } from 'zod';
22

3-
import dbTools, { configureHost, createDbTools, getHost, toolSchema } from '../src';
3+
import constructiveDbTools, {
4+
configureHost,
5+
createConstructiveDbTools,
6+
getHost,
7+
toolSchema
8+
} from '../src';
49

510
describe('host configuration', () => {
611
it('getHost throws before configureHost', () => {
@@ -17,12 +22,12 @@ describe('host configuration', () => {
1722
expect(getHost().account()?.userId).toBe('u1');
1823
});
1924

20-
it('createDbTools configures the host and returns the extension', () => {
25+
it('createConstructiveDbTools configures the host and returns the tools', () => {
2126
const host = {
2227
account: (): null => null,
2328
backendConfig: (): null => null,
2429
};
25-
expect(createDbTools(host)).toBe(dbTools);
30+
expect(createConstructiveDbTools(host)).toBe(constructiveDbTools);
2631
expect(getHost()).toBe(host);
2732
});
2833
});
@@ -44,18 +49,10 @@ describe('toolSchema', () => {
4449
});
4550
});
4651

47-
describe('dbTools extension', () => {
48-
it('registers the 18 tools and both gate events', () => {
49-
const registered: string[] = [];
50-
const events: string[] = [];
51-
const fakePi = {
52-
registerTool: (tool: { name: string }) => registered.push(tool.name),
53-
on: (event: string) => events.push(event),
54-
};
55-
(dbTools as (pi: unknown) => void)(fakePi);
56-
57-
expect(registered).toHaveLength(18);
58-
expect(registered).toEqual(
52+
describe('constructiveDbTools', () => {
53+
it('is the 18 db tools, harness-neutral', () => {
54+
expect(constructiveDbTools).toHaveLength(18);
55+
expect(constructiveDbTools.map((tool) => tool.name)).toEqual(
5956
expect.arrayContaining([
6057
'provision_database',
6158
'provision_blueprint',
@@ -77,6 +74,9 @@ describe('dbTools extension', () => {
7774
'delete_template',
7875
]),
7976
);
80-
expect(events).toEqual(['agent_start', 'tool_call']);
77+
for (const tool of constructiveDbTools) {
78+
expect(typeof tool.execute).toBe('function');
79+
expect(tool.parameters).toBeDefined();
80+
}
8181
});
8282
});
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)