@dawn-ai/core 0.8.7 → 0.8.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/README.md +149 -1
- package/dist/capabilities/built-in/memory.d.ts.map +1 -1
- package/dist/capabilities/built-in/memory.js +82 -13
- package/dist/capabilities/permission-gate.d.ts +43 -0
- package/dist/capabilities/permission-gate.d.ts.map +1 -1
- package/dist/capabilities/permission-gate.js +109 -8
- package/dist/capabilities/types.d.ts +43 -2
- package/dist/capabilities/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +15 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -6,7 +6,155 @@
|
|
|
6
6
|
|
|
7
7
|
Filesystem-based route discovery, app config loading, state-field resolution, and typegen primitives that the Dawn CLI builds on.
|
|
8
8
|
|
|
9
|
-
This is
|
|
9
|
+
This is part of [Dawn - the TypeScript meta-framework for LangGraph](https://github.com/cacheplane/dawnai).
|
|
10
|
+
Use this package for CLI/runtime integration points; application routes usually
|
|
11
|
+
import author-facing helpers from `@dawn-ai/sdk`.
|
|
12
|
+
|
|
13
|
+
Conceptual docs: [Routes](https://dawnai.org/docs/routes),
|
|
14
|
+
[Configuration](https://dawnai.org/docs/configuration),
|
|
15
|
+
[Workspace Filesystem](https://dawnai.org/docs/workspace),
|
|
16
|
+
[Memory](https://dawnai.org/docs/memory), and
|
|
17
|
+
[API Reference](https://dawnai.org/docs/api#dawn-aicore).
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @dawn-ai/core
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
createCapabilityRegistry,
|
|
28
|
+
createWorkspaceFs,
|
|
29
|
+
discoverRoutes,
|
|
30
|
+
loadDawnConfig,
|
|
31
|
+
renderDawnTypes,
|
|
32
|
+
type DawnConfig,
|
|
33
|
+
} from "@dawn-ai/core"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Public Exports
|
|
37
|
+
|
|
38
|
+
### Capability markers
|
|
39
|
+
|
|
40
|
+
- `createAgentsMdMarker()`
|
|
41
|
+
- `createMemoryMarker()`
|
|
42
|
+
- `createMemoryMdMarker()`
|
|
43
|
+
- `createPlanningMarker()`
|
|
44
|
+
- `createSkillsMarker()`
|
|
45
|
+
- `createSubagentsMarker()`
|
|
46
|
+
- `createWorkspaceMarker()`
|
|
47
|
+
- `BUILT_IN_TOOL_NAMES`
|
|
48
|
+
|
|
49
|
+
These markers detect route/app files and contribute prompt fragments, tools, or
|
|
50
|
+
stream transformers. `createMemoryMarker()` activates only when route memory
|
|
51
|
+
context is supplied; `createWorkspaceMarker()` activates when a `workspace/`
|
|
52
|
+
directory exists or a runtime `workspaceRoot` is supplied.
|
|
53
|
+
|
|
54
|
+
### Capability registry and permission gating
|
|
55
|
+
|
|
56
|
+
- `createCapabilityRegistry(markers)` and `applyCapabilities()` collect marker
|
|
57
|
+
contributions for a route.
|
|
58
|
+
- `gateToolOp()` and `wrapToolWithApproval()` apply tool-level approval rules.
|
|
59
|
+
- Types include `CapabilityMarker`, `CapabilityMarkerContext`,
|
|
60
|
+
`CapabilityContribution`, `DawnToolDefinition`, `PromptFragment`,
|
|
61
|
+
`StreamTransformer`, `MemoryContext`, `MemoryStoreLike`, and registry result
|
|
62
|
+
types.
|
|
63
|
+
|
|
64
|
+
### Workspace filesystem
|
|
65
|
+
|
|
66
|
+
- `createWorkspaceFs(options)` builds the `WorkspaceFs` handle used as `ctx.fs`.
|
|
67
|
+
- `CreateWorkspaceFsOptions` accepts `workspaceRoot`, a `FilesystemBackend`,
|
|
68
|
+
permissions, an `AbortSignal`, and `interruptCapable`.
|
|
69
|
+
|
|
70
|
+
The helper resolves paths relative to `workspaceRoot`, canonicalizes them with
|
|
71
|
+
the backend's `realPath`, and applies the same permission gate used by
|
|
72
|
+
agent-facing workspace tools.
|
|
73
|
+
|
|
74
|
+
### Configuration and discovery
|
|
75
|
+
|
|
76
|
+
- `loadDawnConfig({ appRoot })` loads and normalizes `dawn.config.ts`.
|
|
77
|
+
- `config(value)` is the typed identity helper for config files.
|
|
78
|
+
- `discoverRoutes(options)` scans the Dawn app directory and returns a route
|
|
79
|
+
manifest.
|
|
80
|
+
- `findDawnApp(options)` locates app roots, and
|
|
81
|
+
`assertDawnRoutesDir(appRoot, routesDir?)` validates that a routes directory
|
|
82
|
+
exists, returning `Promise<void>`.
|
|
83
|
+
- `toRouteSegments()`, `isPrivateSegment()`, and `isRouteGroupSegment()` parse
|
|
84
|
+
file-system route segments.
|
|
85
|
+
- Types include `DawnConfig`, `LoadedDawnConfig`, `DiscoverRoutesOptions`,
|
|
86
|
+
`DiscoveredDawnApp`, `RouteDefinition`, `RouteManifest`, `RouteSegment`,
|
|
87
|
+
`RouteKind`, and `NormalizedRouteModule`.
|
|
88
|
+
|
|
89
|
+
### State and typegen
|
|
90
|
+
|
|
91
|
+
- `resolveStateFields()` resolves route state reducers and defaults.
|
|
92
|
+
- `extractToolSchemasForRoute()` and `extractToolTypesForRoute()` inspect route
|
|
93
|
+
tools for schemas and generated type definitions.
|
|
94
|
+
- `renderDawnTypes()`, `renderRouteTypes()`, `renderStateTypes()`, and
|
|
95
|
+
`renderToolTypes()` render the generated `dawn:routes` declaration. Some docs
|
|
96
|
+
and audits refer to this group as `renderTypeDefinitions`; there is no single
|
|
97
|
+
exported function by that name.
|
|
98
|
+
- Types include `ResolveStateFieldsOptions`, `RouteStateFields`,
|
|
99
|
+
`ExtractToolSchemasOptions`, `ExtractToolTypesOptions`,
|
|
100
|
+
`RouteToolSchemas`, `RouteToolTypes`, `ExtractedToolSchema`,
|
|
101
|
+
`ExtractedToolType`, `ResolvedStateField`, and `StateFieldReducer`.
|
|
102
|
+
|
|
103
|
+
### Tool scope
|
|
104
|
+
|
|
105
|
+
- `resolveToolScope()` and `toolOrigin()` decide whether a discovered tool is
|
|
106
|
+
visible to a route.
|
|
107
|
+
- Types include `ScopeInput` and `ToolOrigin`.
|
|
108
|
+
|
|
109
|
+
### Storage type re-export
|
|
110
|
+
|
|
111
|
+
- `ThreadsStore` is re-exported from `@dawn-ai/sqlite-storage` for config typing.
|
|
112
|
+
|
|
113
|
+
## Examples
|
|
114
|
+
|
|
115
|
+
Discover routes and render generated route types:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import {
|
|
119
|
+
discoverRoutes,
|
|
120
|
+
extractToolTypesForRoute,
|
|
121
|
+
renderDawnTypes,
|
|
122
|
+
} from "@dawn-ai/core"
|
|
123
|
+
|
|
124
|
+
const manifest = await discoverRoutes({ appRoot: process.cwd() })
|
|
125
|
+
const toolTypes = await Promise.all(
|
|
126
|
+
manifest.routes.map(async (route) => ({
|
|
127
|
+
pathname: route.pathname,
|
|
128
|
+
tools: await extractToolTypesForRoute({
|
|
129
|
+
routeDir: route.routeDir,
|
|
130
|
+
sharedToolsDir: `${process.cwd()}/src`,
|
|
131
|
+
}),
|
|
132
|
+
})),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const dts = renderDawnTypes(manifest, toolTypes)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Create a gated `WorkspaceFs` handle:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { createWorkspaceFs } from "@dawn-ai/core"
|
|
142
|
+
import { localFilesystem } from "@dawn-ai/workspace"
|
|
143
|
+
|
|
144
|
+
const fs = createWorkspaceFs({
|
|
145
|
+
workspaceRoot: `${process.cwd()}/workspace`,
|
|
146
|
+
backend: localFilesystem(),
|
|
147
|
+
permissions: undefined,
|
|
148
|
+
signal: new AbortController().signal,
|
|
149
|
+
interruptCapable: false,
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Notes
|
|
154
|
+
|
|
155
|
+
`@dawn-ai/core` is intentionally lower level than `@dawn-ai/sdk`. Prefer the SDK
|
|
156
|
+
for route code (`agent`, `defineMemory`, `DawnToolContext`, `RuntimeContext`),
|
|
157
|
+
and use core when building CLIs, adapters, tests, or runtime integrations.
|
|
10
158
|
|
|
11
159
|
## License
|
|
12
160
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/capabilities/built-in/memory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/capabilities/built-in/memory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,aAAa,CAAA;AAanE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,gBAAgB,CAsPrD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { gateMemorySupersede } from "../permission-gate.js";
|
|
3
4
|
const DEFAULT_SEMANTIC_IDENTITY = ["subject", "predicate"];
|
|
4
5
|
// A route's defineMemory() schema arrives as `unknown` (loaded via dynamic
|
|
5
6
|
// import, validated structurally). Module-scoped (no closure deps) so it isn't
|
|
@@ -23,6 +24,7 @@ export function createMemoryMarker() {
|
|
|
23
24
|
const mem = context.memory;
|
|
24
25
|
if (!mem)
|
|
25
26
|
return {};
|
|
27
|
+
const permissions = context.permissions;
|
|
26
28
|
const indexEntries = await mem.store.search({
|
|
27
29
|
namespace: mem.namespace,
|
|
28
30
|
status: "active",
|
|
@@ -56,6 +58,25 @@ export function createMemoryMarker() {
|
|
|
56
58
|
schema: recallSchema,
|
|
57
59
|
run: async (input) => {
|
|
58
60
|
const q = (input ?? {});
|
|
61
|
+
// Embed the query for the hybrid keyword+vector path when an embedder
|
|
62
|
+
// is configured. Embed FAILURE degrades to keyword-only — never throw,
|
|
63
|
+
// so a flaky/offline embedder can't break recall.
|
|
64
|
+
let queryVec;
|
|
65
|
+
if (mem.embedder && q.query) {
|
|
66
|
+
try {
|
|
67
|
+
;
|
|
68
|
+
[queryVec] = await mem.embedder.embed([q.query]);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
// Gated: silent embed failures are an ops footgun (user thinks
|
|
72
|
+
// vector recall works while every embed errors). Mirrors the
|
|
73
|
+
// summarization hook's DAWN_DEBUG_SUMMARIZATION convention.
|
|
74
|
+
if (process.env.DAWN_DEBUG_MEMORY === "1") {
|
|
75
|
+
console.warn(`[dawn:memory] recall embed failed, falling back to keyword-only: ${String(err)}`);
|
|
76
|
+
}
|
|
77
|
+
queryVec = undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
59
80
|
const rows = await mem.store.search({
|
|
60
81
|
namespace: mem.namespace,
|
|
61
82
|
...(q.query ? { query: q.query } : {}),
|
|
@@ -65,10 +86,16 @@ export function createMemoryMarker() {
|
|
|
65
86
|
// Recency reference for ranked recall — the per-request timestamp,
|
|
66
87
|
// NOT Date.now() (determinism rule; see module docblock).
|
|
67
88
|
now: mem.now,
|
|
89
|
+
...(queryVec && mem.embedder
|
|
90
|
+
? { queryEmbedding: queryVec, embedderId: mem.embedder.id, vector: mem.vector }
|
|
91
|
+
: {}),
|
|
68
92
|
});
|
|
93
|
+
// Wrap in {result} so the langchain bridge uses the string verbatim as
|
|
94
|
+
// the ToolMessage content; a bare string hits unwrapToolResult's
|
|
95
|
+
// JSON.stringify path, quoting it and escaping the newlines below.
|
|
69
96
|
if (rows.length === 0)
|
|
70
|
-
return "(no memories found)";
|
|
71
|
-
return rows.map((r) => `${r.id}: ${r.content}`).join("\n");
|
|
97
|
+
return { result: "(no memories found)" };
|
|
98
|
+
return { result: rows.map((r) => `${r.id}: ${r.content}`).join("\n") };
|
|
72
99
|
},
|
|
73
100
|
};
|
|
74
101
|
const remember = {
|
|
@@ -78,8 +105,9 @@ export function createMemoryMarker() {
|
|
|
78
105
|
run: async (input) => {
|
|
79
106
|
const inp = (input ?? {});
|
|
80
107
|
const validated = mem.validate(inp.data);
|
|
108
|
+
// All returns below wrap in {result} — see the recall tool's note.
|
|
81
109
|
if (!validated.ok)
|
|
82
|
-
return `Rejected: ${validated.errors}
|
|
110
|
+
return { result: `Rejected: ${validated.errors}` };
|
|
83
111
|
const data = validated.value;
|
|
84
112
|
const identityKeys = mem.defined.identity ?? DEFAULT_SEMANTIC_IDENTITY;
|
|
85
113
|
// id is DATA-derived so contradicting values (same identity, different
|
|
@@ -88,7 +116,9 @@ export function createMemoryMarker() {
|
|
|
88
116
|
.update(`${mem.namespace}|${JSON.stringify(data)}`)
|
|
89
117
|
.digest("hex")
|
|
90
118
|
.slice(0, 16)}`;
|
|
91
|
-
|
|
119
|
+
// "ask" shares auto's write semantics; only its SUPERSEDE branch gates.
|
|
120
|
+
const autoLike = mem.writes === "auto" || mem.writes === "ask";
|
|
121
|
+
const status = autoLike ? "active" : "candidate";
|
|
92
122
|
const content = typeof inp.content === "string" && inp.content.length > 0
|
|
93
123
|
? inp.content
|
|
94
124
|
: JSON.stringify(data);
|
|
@@ -107,7 +137,26 @@ export function createMemoryMarker() {
|
|
|
107
137
|
createdAt: mem.now,
|
|
108
138
|
updatedAt: mem.now,
|
|
109
139
|
};
|
|
110
|
-
|
|
140
|
+
// Embed the content for vector recall when an embedder is configured.
|
|
141
|
+
// Embed FAILURE degrades to keyword-only (putOpts stays undefined) —
|
|
142
|
+
// NEVER lose the write. Forwarded to EVERY put site below.
|
|
143
|
+
let putOpts;
|
|
144
|
+
if (mem.embedder) {
|
|
145
|
+
try {
|
|
146
|
+
const [ev] = await mem.embedder.embed([content]);
|
|
147
|
+
if (ev)
|
|
148
|
+
putOpts = { embedding: ev, embeddingModel: mem.embedder.id };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
// Gated warn — see the recall catch above. The write still lands
|
|
152
|
+
// keyword-only (putOpts undefined); we never lose the memory.
|
|
153
|
+
if (process.env.DAWN_DEBUG_MEMORY === "1") {
|
|
154
|
+
console.warn(`[dawn:memory] remember embed failed, storing keyword-only: ${String(err)}`);
|
|
155
|
+
}
|
|
156
|
+
putOpts = undefined;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (autoLike) {
|
|
111
160
|
// Inline identity key helper — avoids importing from @dawn-ai/memory
|
|
112
161
|
const identityKey = (d) => identityKeys.map((k) => JSON.stringify(d[k] ?? null)).join(" ");
|
|
113
162
|
const existing = await mem.store.search({
|
|
@@ -125,21 +174,41 @@ export function createMemoryMarker() {
|
|
|
125
174
|
confidence,
|
|
126
175
|
tags,
|
|
127
176
|
});
|
|
128
|
-
return `Updated memory ${target.id}
|
|
177
|
+
return { result: `Updated memory ${target.id}.` };
|
|
178
|
+
}
|
|
179
|
+
// Same identity but different value — supersede. In "ask" mode this
|
|
180
|
+
// is the one write that gates: the agent is contradicting a prior
|
|
181
|
+
// belief. ADDs/idempotent UPDATEs above never reach the gate.
|
|
182
|
+
if (mem.writes === "ask") {
|
|
183
|
+
const gate = await gateMemorySupersede(permissions, {
|
|
184
|
+
namespace: mem.namespace,
|
|
185
|
+
// Human-readable display form for the prompt — deliberately NOT
|
|
186
|
+
// the `identityKey` match key above (which JSON.stringifies to
|
|
187
|
+
// stay unambiguous); do not merge the two.
|
|
188
|
+
identity: identityKeys.map((k) => String(data[k] ?? "")).join(" / "),
|
|
189
|
+
oldId: target.id,
|
|
190
|
+
oldContent: target.content,
|
|
191
|
+
newContent: content,
|
|
192
|
+
});
|
|
193
|
+
if (!gate.allowed) {
|
|
194
|
+
return {
|
|
195
|
+
result: `Kept existing memory ${target.id} ("${target.content}"); ` +
|
|
196
|
+
`your contradicting value was not stored (${gate.reason}).`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
129
199
|
}
|
|
130
|
-
|
|
131
|
-
await mem.store.put(record);
|
|
200
|
+
await mem.store.put(record, putOpts);
|
|
132
201
|
await mem.store.supersede(target.id, id);
|
|
133
|
-
return `Superseded ${target.id} with ${id}
|
|
202
|
+
return { result: `Superseded ${target.id} with ${id}.` };
|
|
134
203
|
}
|
|
135
204
|
// No existing record with same identity — add new active row
|
|
136
|
-
await mem.store.put(record);
|
|
137
|
-
return `Stored memory ${id}
|
|
205
|
+
await mem.store.put(record, putOpts);
|
|
206
|
+
return { result: `Stored memory ${id}.` };
|
|
138
207
|
}
|
|
139
208
|
// Candidate mode (and "off" never reaches here — remember tool absent):
|
|
140
209
|
// write a candidate; reconciliation happens later at CLI approval.
|
|
141
|
-
await mem.store.put(record);
|
|
142
|
-
return `Stored memory candidate ${id} (pending approval)
|
|
210
|
+
await mem.store.put(record, putOpts);
|
|
211
|
+
return { result: `Stored memory candidate ${id} (pending approval).` };
|
|
143
212
|
},
|
|
144
213
|
};
|
|
145
214
|
// Fingerprint the snapshot the render closure froze at load time. `id`
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PermissionsStore } from "@dawn-ai/permissions";
|
|
2
|
+
import type { ConstraintPredicate } from "@dawn-ai/sdk";
|
|
2
3
|
export type PathOperation = "readFile" | "writeFile" | "listDir";
|
|
3
4
|
export type GateResult = {
|
|
4
5
|
allowed: true;
|
|
@@ -19,6 +20,28 @@ export declare function gateBashOp(permissions: PermissionsStore | undefined, co
|
|
|
19
20
|
export declare function gateToolOp(permissions: PermissionsStore | undefined, toolName: string, argsPreview: string, opts?: {
|
|
20
21
|
readonly interruptCapable?: boolean;
|
|
21
22
|
}): Promise<GateResult>;
|
|
23
|
+
export interface MemorySupersedeDetail {
|
|
24
|
+
readonly namespace: string;
|
|
25
|
+
readonly identity: string;
|
|
26
|
+
readonly oldId: string;
|
|
27
|
+
readonly oldContent: string;
|
|
28
|
+
readonly newContent: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Memory supersede gate (memory.writes: "ask"). Prompts ONLY when the agent
|
|
32
|
+
* contradicts an existing active memory — ADDs and idempotent UPDATEs never
|
|
33
|
+
* reach this gate. Persisted decisions live under the reserved "memory" key
|
|
34
|
+
* as workspace+route namespace prefixes; candidates are matched with a "|"
|
|
35
|
+
* terminator so sibling routes cannot prefix-collide.
|
|
36
|
+
*
|
|
37
|
+
* DELIBERATE DIVERGENCE from gateToolOp: on "unknown" with no interactive
|
|
38
|
+
* human (non-interactive mode), this gate ALLOWS the supersede — ask is a
|
|
39
|
+
* supervision affordance, not a security boundary; headless it behaves
|
|
40
|
+
* exactly as writes:"auto". Explicit deny rules are still honored headless.
|
|
41
|
+
* Only called from inside the memory capability's remember tool, which only
|
|
42
|
+
* exists on agent routes (in-graph), so interrupt() is safe here.
|
|
43
|
+
*/
|
|
44
|
+
export declare function gateMemorySupersede(permissions: PermissionsStore | undefined, detail: MemorySupersedeDetail): Promise<GateResult>;
|
|
22
45
|
/**
|
|
23
46
|
* Wrap a tool so each call passes gateToolOp first (tools.approve). A blocked
|
|
24
47
|
* call returns the denial reason AS THE TOOL RESULT — deliberately a different
|
|
@@ -43,4 +66,24 @@ export declare function wrapToolWithApproval<C, T extends {
|
|
|
43
66
|
}>(tool: T, permissions: PermissionsStore, opts?: {
|
|
44
67
|
readonly interruptCapable?: boolean;
|
|
45
68
|
}): T;
|
|
69
|
+
/**
|
|
70
|
+
* Wrap a tool so each call is first evaluated by an argument-constraint predicate
|
|
71
|
+
* (tools.constrain). The predicate returns `true` (allow), a string (deny — the
|
|
72
|
+
* string is returned as the tool result, matching wrapToolWithApproval's
|
|
73
|
+
* return-not-throw contract), or `{ approve: true }` (escalate to the HITL gate
|
|
74
|
+
* via gateToolOp). A predicate that throws OR returns any off-contract value
|
|
75
|
+
* (false, undefined, `{ approve: false }`, …) fails closed (deny) — a broken
|
|
76
|
+
* policy never silently allows. Per-call identity (signal/threadId/params) is read from
|
|
77
|
+
* the LIVE run context, never closed over, so the wrapper is safe inside the
|
|
78
|
+
* per-descriptor-cached agent. `routeId` and `predicate` are stable per descriptor
|
|
79
|
+
* and closed over.
|
|
80
|
+
*/
|
|
81
|
+
export declare function wrapToolWithConstraint<C extends {
|
|
82
|
+
readonly signal: AbortSignal;
|
|
83
|
+
readonly threadId?: string;
|
|
84
|
+
readonly params?: Readonly<Record<string, string>>;
|
|
85
|
+
}, T extends {
|
|
86
|
+
readonly name: string;
|
|
87
|
+
readonly run: (input: unknown, context: C) => Promise<unknown> | unknown;
|
|
88
|
+
}>(tool: T, predicate: ConstraintPredicate, permissions: PermissionsStore | undefined, routeId: string): T;
|
|
46
89
|
//# sourceMappingURL=permission-gate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission-gate.d.ts","sourceRoot":"","sources":["../../src/capabilities/permission-gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"permission-gate.d.ts","sourceRoot":"","sources":["../../src/capabilities/permission-gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAM5D,OAAO,KAAK,EAAqB,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAG1E,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAA;AAEhE,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/E,wBAAsB,UAAU,CAC9B,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,OAAO,CAAC,UAAU,CAAC,CAwCrB;AAED,wBAAsB,UAAU,CAC9B,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC,CAqBrB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,OAAO,CAAC,UAAU,CAAC,CA+BrB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,UAAU,CAAC,CAqBrB;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EACD,CAAC,SAAS;IACR,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;CACzE,EACD,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,CAAC,CAS3F;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,SAAS;IACR,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACnD,EACD,CAAC,SAAS;IACR,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;CACzE,EAED,IAAI,EAAE,CAAC,EACP,SAAS,EAAE,mBAAmB,EAC9B,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,OAAO,EAAE,MAAM,GACd,CAAC,CAkCH"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sep } from "node:path";
|
|
2
|
-
import { suggestedCommandPattern, suggestedPathPattern } from "@dawn-ai/permissions";
|
|
2
|
+
import { suggestedCommandPattern, suggestedMemoryPattern, suggestedPathPattern, } from "@dawn-ai/permissions";
|
|
3
3
|
import { interrupt } from "@langchain/langgraph";
|
|
4
4
|
export async function gatePathOp(permissions, operation, absPath, workspaceRoot, opts) {
|
|
5
5
|
// If permissions store is absent, allow (legacy behavior — capability used without permissions context).
|
|
@@ -105,6 +105,44 @@ export async function gateToolOp(permissions, toolName, argsPreview, opts) {
|
|
|
105
105
|
}
|
|
106
106
|
return { allowed: true };
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Memory supersede gate (memory.writes: "ask"). Prompts ONLY when the agent
|
|
110
|
+
* contradicts an existing active memory — ADDs and idempotent UPDATEs never
|
|
111
|
+
* reach this gate. Persisted decisions live under the reserved "memory" key
|
|
112
|
+
* as workspace+route namespace prefixes; candidates are matched with a "|"
|
|
113
|
+
* terminator so sibling routes cannot prefix-collide.
|
|
114
|
+
*
|
|
115
|
+
* DELIBERATE DIVERGENCE from gateToolOp: on "unknown" with no interactive
|
|
116
|
+
* human (non-interactive mode), this gate ALLOWS the supersede — ask is a
|
|
117
|
+
* supervision affordance, not a security boundary; headless it behaves
|
|
118
|
+
* exactly as writes:"auto". Explicit deny rules are still honored headless.
|
|
119
|
+
* Only called from inside the memory capability's remember tool, which only
|
|
120
|
+
* exists on agent routes (in-graph), so interrupt() is safe here.
|
|
121
|
+
*/
|
|
122
|
+
export async function gateMemorySupersede(permissions, detail) {
|
|
123
|
+
if (!permissions)
|
|
124
|
+
return { allowed: true };
|
|
125
|
+
if (permissions.mode === "bypass")
|
|
126
|
+
return { allowed: true };
|
|
127
|
+
const decision = permissions.match("memory", `${detail.namespace}|`);
|
|
128
|
+
if (decision === "allow")
|
|
129
|
+
return { allowed: true };
|
|
130
|
+
if (decision === "deny") {
|
|
131
|
+
return { allowed: false, reason: `approval denied for this route's memory overwrites` };
|
|
132
|
+
}
|
|
133
|
+
// unknown + headless → allow through (ask ≡ auto without a human).
|
|
134
|
+
if (permissions.mode === "non-interactive")
|
|
135
|
+
return { allowed: true };
|
|
136
|
+
const result = await emitPermissionInterrupt({
|
|
137
|
+
kind: "memory",
|
|
138
|
+
...detail,
|
|
139
|
+
permissions,
|
|
140
|
+
});
|
|
141
|
+
if (result === "deny") {
|
|
142
|
+
return { allowed: false, reason: `approval denied` };
|
|
143
|
+
}
|
|
144
|
+
return { allowed: true };
|
|
145
|
+
}
|
|
108
146
|
/** Best-effort display preview of a tool call's args. Never matched or persisted. */
|
|
109
147
|
function buildArgsPreview(input) {
|
|
110
148
|
try {
|
|
@@ -144,13 +182,65 @@ export function wrapToolWithApproval(tool, permissions, opts) {
|
|
|
144
182
|
},
|
|
145
183
|
};
|
|
146
184
|
}
|
|
185
|
+
const CONSTRAINT_FAILED_REASON = "Blocked: the tool's argument constraint check failed (the policy predicate threw or returned an invalid verdict). Not run.";
|
|
186
|
+
/**
|
|
187
|
+
* Wrap a tool so each call is first evaluated by an argument-constraint predicate
|
|
188
|
+
* (tools.constrain). The predicate returns `true` (allow), a string (deny — the
|
|
189
|
+
* string is returned as the tool result, matching wrapToolWithApproval's
|
|
190
|
+
* return-not-throw contract), or `{ approve: true }` (escalate to the HITL gate
|
|
191
|
+
* via gateToolOp). A predicate that throws OR returns any off-contract value
|
|
192
|
+
* (false, undefined, `{ approve: false }`, …) fails closed (deny) — a broken
|
|
193
|
+
* policy never silently allows. Per-call identity (signal/threadId/params) is read from
|
|
194
|
+
* the LIVE run context, never closed over, so the wrapper is safe inside the
|
|
195
|
+
* per-descriptor-cached agent. `routeId` and `predicate` are stable per descriptor
|
|
196
|
+
* and closed over.
|
|
197
|
+
*/
|
|
198
|
+
export function wrapToolWithConstraint(tool, predicate, permissions, routeId) {
|
|
199
|
+
return {
|
|
200
|
+
...tool,
|
|
201
|
+
run: async (input, context) => {
|
|
202
|
+
const ctx = {
|
|
203
|
+
toolName: tool.name,
|
|
204
|
+
routeId,
|
|
205
|
+
signal: context.signal,
|
|
206
|
+
...(context.threadId ? { threadId: context.threadId } : {}),
|
|
207
|
+
...(context.params ? { params: context.params } : {}),
|
|
208
|
+
};
|
|
209
|
+
let verdict;
|
|
210
|
+
try {
|
|
211
|
+
verdict = await predicate(input, ctx);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return CONSTRAINT_FAILED_REASON;
|
|
215
|
+
}
|
|
216
|
+
if (verdict === true)
|
|
217
|
+
return tool.run(input, context);
|
|
218
|
+
if (typeof verdict === "string")
|
|
219
|
+
return verdict;
|
|
220
|
+
// Escalate to HITL ONLY on a genuine { approve: true } verdict.
|
|
221
|
+
if (typeof verdict === "object" &&
|
|
222
|
+
verdict !== null &&
|
|
223
|
+
verdict.approve === true) {
|
|
224
|
+
const gate = await gateToolOp(permissions, tool.name, buildArgsPreview(input));
|
|
225
|
+
if (!gate.allowed)
|
|
226
|
+
return gate.reason;
|
|
227
|
+
return tool.run(input, context);
|
|
228
|
+
}
|
|
229
|
+
// Any other value (false, undefined, { approve: false }, a number, …) is
|
|
230
|
+
// off-contract — fail closed rather than silently escalate or allow.
|
|
231
|
+
return CONSTRAINT_FAILED_REASON;
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
147
235
|
async function emitPermissionInterrupt(args) {
|
|
148
236
|
const interruptId = `perm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
149
237
|
const suggestedPattern = args.kind === "command"
|
|
150
238
|
? suggestedCommandPattern(args.command)
|
|
151
239
|
: args.kind === "tool"
|
|
152
240
|
? args.toolName
|
|
153
|
-
:
|
|
241
|
+
: args.kind === "memory"
|
|
242
|
+
? suggestedMemoryPattern(args.namespace)
|
|
243
|
+
: suggestedPathPattern(args.path);
|
|
154
244
|
const payload = {
|
|
155
245
|
interruptId,
|
|
156
246
|
type: "permission-request",
|
|
@@ -159,17 +249,28 @@ async function emitPermissionInterrupt(args) {
|
|
|
159
249
|
? { command: args.command, suggestedPattern }
|
|
160
250
|
: args.kind === "tool"
|
|
161
251
|
? { toolName: args.toolName, argsPreview: args.argsPreview, suggestedPattern }
|
|
162
|
-
:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
252
|
+
: args.kind === "memory"
|
|
253
|
+
? {
|
|
254
|
+
namespace: args.namespace,
|
|
255
|
+
identity: args.identity,
|
|
256
|
+
oldId: args.oldId,
|
|
257
|
+
oldContent: args.oldContent,
|
|
258
|
+
newContent: args.newContent,
|
|
259
|
+
suggestedPattern,
|
|
260
|
+
}
|
|
261
|
+
: { operation: args.operation, path: args.path, suggestedPattern },
|
|
167
262
|
};
|
|
168
263
|
const decision = interrupt(payload);
|
|
169
264
|
if (decision === "deny")
|
|
170
265
|
return "deny";
|
|
171
266
|
if (decision === "always") {
|
|
172
|
-
const tool = args.kind === "command"
|
|
267
|
+
const tool = args.kind === "command"
|
|
268
|
+
? "bash"
|
|
269
|
+
: args.kind === "tool"
|
|
270
|
+
? "tool"
|
|
271
|
+
: args.kind === "memory"
|
|
272
|
+
? "memory"
|
|
273
|
+
: args.operation;
|
|
173
274
|
await args.permissions.addAllow(tool, suggestedPattern);
|
|
174
275
|
}
|
|
175
276
|
return "allow";
|
|
@@ -19,8 +19,20 @@ export interface MemoryRecordLike {
|
|
|
19
19
|
readonly updatedAt: string;
|
|
20
20
|
readonly supersedes?: readonly string[];
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Pluggable text embedder for opt-in vector/semantic recall. Structural — the
|
|
24
|
+
* concrete implementations (`openaiEmbedder`, `fakeEmbedder`) live outside core.
|
|
25
|
+
*/
|
|
26
|
+
export interface Embedder {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly dims: number;
|
|
29
|
+
embed(texts: readonly string[]): Promise<Float32Array[]>;
|
|
30
|
+
}
|
|
22
31
|
export interface MemoryStoreLike {
|
|
23
|
-
put(rec: MemoryRecordLike
|
|
32
|
+
put(rec: MemoryRecordLike, opts?: {
|
|
33
|
+
embedding?: Float32Array;
|
|
34
|
+
embeddingModel?: string;
|
|
35
|
+
}): Promise<void>;
|
|
24
36
|
get(id: string): Promise<MemoryRecordLike | null>;
|
|
25
37
|
search(q: {
|
|
26
38
|
namespace: string;
|
|
@@ -31,14 +43,26 @@ export interface MemoryStoreLike {
|
|
|
31
43
|
limit?: number;
|
|
32
44
|
/** ISO recency reference for ranked searches; stores may ignore it. */
|
|
33
45
|
now?: string;
|
|
46
|
+
/** When present, the store runs the hybrid keyword+vector path. */
|
|
47
|
+
queryEmbedding?: Float32Array;
|
|
48
|
+
/** Only rows whose stored embedding model equals this are vector-compared. */
|
|
49
|
+
embedderId?: string;
|
|
50
|
+
/** Hybrid ranking tuning; structural — the store validates. */
|
|
51
|
+
vector?: unknown;
|
|
34
52
|
}): Promise<readonly MemoryRecordLike[]>;
|
|
35
53
|
update(id: string, patch: Partial<MemoryRecordLike>): Promise<void>;
|
|
36
54
|
supersede(id: string, bySupersedingId: string): Promise<void>;
|
|
37
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Memory write-governance mode. "ask" = auto's exact write semantics, except
|
|
58
|
+
* SUPERSEDEs (same identity, different value) pass a HITL gate first — a
|
|
59
|
+
* supervision affordance, not a security boundary (headless ≡ auto).
|
|
60
|
+
*/
|
|
61
|
+
export type MemoryWritesMode = "off" | "candidate" | "auto" | "ask";
|
|
38
62
|
export interface MemoryContext {
|
|
39
63
|
readonly store: MemoryStoreLike;
|
|
40
64
|
readonly namespace: string;
|
|
41
|
-
readonly writes:
|
|
65
|
+
readonly writes: MemoryWritesMode;
|
|
42
66
|
readonly defined: {
|
|
43
67
|
readonly kind: string;
|
|
44
68
|
readonly scope: readonly string[];
|
|
@@ -55,6 +79,21 @@ export interface MemoryContext {
|
|
|
55
79
|
};
|
|
56
80
|
readonly now: string;
|
|
57
81
|
readonly indexMaxEntries?: number;
|
|
82
|
+
/** The resolved embedder when vector recall is enabled; the capability embeds
|
|
83
|
+
* writes + queries through it. Absent → keyword-only. */
|
|
84
|
+
readonly embedder?: Embedder;
|
|
85
|
+
/** Hybrid recall tuning threaded through to the store's search. All fields
|
|
86
|
+
* optional/defaulted; absent → store defaults. */
|
|
87
|
+
readonly vector?: {
|
|
88
|
+
readonly weights?: {
|
|
89
|
+
readonly keyword?: number;
|
|
90
|
+
readonly vector?: number;
|
|
91
|
+
};
|
|
92
|
+
readonly rrfK?: number;
|
|
93
|
+
readonly vectorK?: number;
|
|
94
|
+
readonly recencyWeight?: number;
|
|
95
|
+
readonly confidenceWeight?: number;
|
|
96
|
+
};
|
|
58
97
|
}
|
|
59
98
|
export interface CapabilityMarkerContext {
|
|
60
99
|
readonly routeManifest: RouteManifest;
|
|
@@ -82,6 +121,8 @@ export interface DawnToolDefinition {
|
|
|
82
121
|
readonly middleware?: Readonly<Record<string, unknown>>;
|
|
83
122
|
readonly signal: AbortSignal;
|
|
84
123
|
readonly fs?: WorkspaceFs;
|
|
124
|
+
readonly threadId?: string;
|
|
125
|
+
readonly params?: Readonly<Record<string, string>>;
|
|
85
126
|
}) => Promise<unknown> | unknown;
|
|
86
127
|
readonly schema?: unknown;
|
|
87
128
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/capabilities/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtC,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/capabilities/types.ts"],"names":[],"mappings":"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"}
|