@dbx-tools/appkit-memory 0.0.0-stage → 0.9.131

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.
Files changed (42) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +78 -2
  3. package/index.ts +17 -0
  4. package/lib/index.d.ts +13 -0
  5. package/lib/index.js +14 -0
  6. package/lib/smoke/mastra-tool.d.ts +1 -0
  7. package/lib/smoke/mastra-tool.js +46 -0
  8. package/lib/src/_contracts.d.ts +71 -0
  9. package/lib/src/_contracts.js +63 -0
  10. package/lib/src/_mastra.d.ts +11 -0
  11. package/lib/src/_mastra.js +66 -0
  12. package/lib/src/config.d.ts +70 -0
  13. package/lib/src/config.js +87 -0
  14. package/lib/src/databricks/_runtime.d.ts +18 -0
  15. package/lib/src/databricks/_runtime.js +79 -0
  16. package/lib/src/databricks/client.d.ts +48 -0
  17. package/lib/src/databricks/client.js +174 -0
  18. package/lib/src/databricks/index.d.ts +3 -0
  19. package/lib/src/databricks/index.js +6 -0
  20. package/lib/src/graphiti/_backend.d.ts +9 -0
  21. package/lib/src/graphiti/_backend.js +214 -0
  22. package/lib/src/graphiti/index.d.ts +3 -0
  23. package/lib/src/graphiti/index.js +6 -0
  24. package/lib/src/graphiti/options.d.ts +7 -0
  25. package/lib/src/graphiti/options.js +7 -0
  26. package/lib/src/graphiti/runtime.d.ts +49 -0
  27. package/lib/src/graphiti/runtime.js +170 -0
  28. package/lib/src/plugin.d.ts +38 -0
  29. package/lib/src/plugin.js +196 -0
  30. package/lib/tsconfig.tsbuildinfo +1 -0
  31. package/package.json +104 -4
  32. package/src/_contracts.ts +107 -0
  33. package/src/_mastra.ts +80 -0
  34. package/src/config.ts +134 -0
  35. package/src/databricks/_runtime.ts +103 -0
  36. package/src/databricks/client.ts +251 -0
  37. package/src/databricks/index.ts +7 -0
  38. package/src/graphiti/_backend.ts +282 -0
  39. package/src/graphiti/index.ts +7 -0
  40. package/src/graphiti/options.ts +17 -0
  41. package/src/graphiti/runtime.ts +261 -0
  42. package/src/plugin.ts +254 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * AppKit memory plugin with Databricks-first tools and lazy Graphiti fallback.
3
+ *
4
+ * @module
5
+ */
6
+ import { getExecutionContext, toPlugin } from "@databricks/appkit";
7
+ import { defineTool, executeFromRegistry, } from "@databricks/appkit/beta";
8
+ import { appkit as dbxAppkit, toolkitEntries, ToolRegistryPlugin, } from "@dbx-tools/appkit";
9
+ import { errorUtils, log } from "@dbx-tools/shared-core";
10
+ import { RecallMemoryInputSchema, RememberMemoryInputSchema, } from "./_contracts.js";
11
+ import { MEMORY_CONFIG_SCHEMA, resolveMemoryPluginConfig, } from "./config.js";
12
+ import { DatabricksMemoryRuntime } from "./databricks/_runtime.js";
13
+ /** AppKit memory plugin with Databricks managed memory and Graphiti fallback. */
14
+ export class MemoryPlugin extends ToolRegistryPlugin {
15
+ static phase = "deferred";
16
+ static manifest = {
17
+ name: "memory",
18
+ displayName: "Memory",
19
+ description: "Publishes Databricks-first durable memory tools with a lazy Graphiti fallback.",
20
+ stability: "beta",
21
+ resources: { required: [], optional: [] },
22
+ config: { schema: MEMORY_CONFIG_SCHEMA },
23
+ };
24
+ logger = log.logger(this);
25
+ resolved;
26
+ databricks;
27
+ graphiti;
28
+ graphitiStarting;
29
+ graphitiStartupAbort = new AbortController();
30
+ closed = false;
31
+ get toolRegistry() {
32
+ return this.registry();
33
+ }
34
+ async setup() {
35
+ const resolved = resolveMemoryPluginConfig(this.config);
36
+ if (resolved.saveTurns === true && resolved.graphiti === false) {
37
+ throw new Error("AppKit memory saveTurns requires the Graphiti fallback");
38
+ }
39
+ this.resolved = resolved;
40
+ if (resolved.databricks) {
41
+ this.databricks = new DatabricksMemoryRuntime(getExecutionContext().client, resolved.databricks);
42
+ }
43
+ let mastra = false;
44
+ const context = this.context;
45
+ const shouldAttachTurnSaver = resolved.saveTurns !== false &&
46
+ resolved.graphiti !== false &&
47
+ context?.hasPlugin("mastra") === true;
48
+ if (shouldAttachTurnSaver && context) {
49
+ const { attachMastraTurnSaver } = await import("./_mastra.js");
50
+ mastra = await attachMastraTurnSaver(context, (turn, signal) => this.saveTurn(turn, signal));
51
+ }
52
+ if (resolved.saveTurns === true && !mastra) {
53
+ throw new Error("AppKit memory saveTurns requires a registered Mastra plugin");
54
+ }
55
+ this.logger.info("ready", {
56
+ databricks: resolved.databricks !== false,
57
+ graphiti: resolved.graphiti !== false ? "lazy" : false,
58
+ mastraTurnSaver: mastra,
59
+ });
60
+ }
61
+ async toolkit(options) {
62
+ return toolkitEntries.entries("memory", this.getAgentTools(), {
63
+ ...options,
64
+ prefix: options?.prefix ?? "",
65
+ });
66
+ }
67
+ executeAgentTool(name, args, signal, context) {
68
+ return executeFromRegistry(this.registry(context?.resourceId), name, args, signal);
69
+ }
70
+ abortActiveOperations() {
71
+ super.abortActiveOperations();
72
+ this.graphitiStartupAbort.abort(new Error("AppKit memory is shutting down"));
73
+ }
74
+ async shutdown() {
75
+ this.closed = true;
76
+ this.graphitiStartupAbort.abort(new Error("AppKit memory is shutting down"));
77
+ const starting = this.graphitiStarting;
78
+ const backend = this.graphiti;
79
+ this.graphitiStarting = undefined;
80
+ this.graphiti = undefined;
81
+ await starting?.catch(() => undefined);
82
+ await backend?.shutdown();
83
+ }
84
+ registry(resourceId) {
85
+ return {
86
+ remember: defineTool({
87
+ description: "Save a durable user preference, fact, decision, or instruction for future conversations.",
88
+ schema: RememberMemoryInputSchema,
89
+ annotations: { effect: "write", requiresUserContext: true },
90
+ autoInheritable: false,
91
+ execute: (input, signal) => this.remember(this.actorId(resourceId), input, signal),
92
+ }),
93
+ recall_memory: defineTool({
94
+ description: "Search durable actor-scoped memory for facts and preferences relevant to the current task.",
95
+ schema: RecallMemoryInputSchema,
96
+ annotations: { effect: "read", requiresUserContext: true },
97
+ autoInheritable: false,
98
+ execute: (input, signal) => this.recall(this.actorId(resourceId), input, signal),
99
+ }),
100
+ };
101
+ }
102
+ async remember(actorId, input, signal) {
103
+ const databricks = await this.databricksBackend(actorId, signal);
104
+ if (databricks) {
105
+ try {
106
+ return await databricks.remember(actorId, input, signal);
107
+ }
108
+ catch (error) {
109
+ if (!this.shouldFallbackFromDatabricks(error))
110
+ throw error;
111
+ }
112
+ }
113
+ return (await this.graphitiBackend()).remember(actorId, input, signal);
114
+ }
115
+ async recall(actorId, input, signal) {
116
+ const parsed = RecallMemoryInputSchema.parse(input);
117
+ const databricks = await this.databricksBackend(actorId, signal);
118
+ if (databricks) {
119
+ try {
120
+ return await databricks.recall(actorId, parsed, signal);
121
+ }
122
+ catch (error) {
123
+ if (!this.shouldFallbackFromDatabricks(error))
124
+ throw error;
125
+ }
126
+ }
127
+ return (await this.graphitiBackend()).recall(actorId, parsed, signal);
128
+ }
129
+ async saveTurn(turn, signal) {
130
+ if (await this.databricksBackend(turn.actorId, signal))
131
+ return;
132
+ await (await this.graphitiBackend()).saveTurn(turn, signal);
133
+ }
134
+ async databricksBackend(actorId, signal) {
135
+ const backend = this.databricks;
136
+ if (!backend)
137
+ return undefined;
138
+ if (await backend.available(actorId, signal))
139
+ return backend;
140
+ const configured = this.requireConfig().databricks;
141
+ if (configured && configured.mode === "required") {
142
+ throw new Error("Required Databricks managed memory is unavailable for the current actor");
143
+ }
144
+ return undefined;
145
+ }
146
+ shouldFallbackFromDatabricks(error) {
147
+ const configured = this.requireConfig().databricks;
148
+ return (configured !== false &&
149
+ configured.mode === "auto" &&
150
+ errorUtils.errorContext(error).hasStatusCode(403, 404));
151
+ }
152
+ async graphitiBackend() {
153
+ if (this.closed)
154
+ throw new Error("AppKit memory is closed");
155
+ if (this.graphiti?.running)
156
+ return this.graphiti;
157
+ if (this.graphiti) {
158
+ await this.graphiti.shutdown();
159
+ this.graphiti = undefined;
160
+ }
161
+ const options = this.requireConfig().graphiti;
162
+ if (!options)
163
+ throw new Error("Graphiti fallback is disabled");
164
+ this.graphitiStarting ??= import("./graphiti/_backend.js")
165
+ .then(({ startGraphitiBackend }) => startGraphitiBackend(options, this.graphitiStartupAbort.signal))
166
+ .then((backend) => {
167
+ if (this.closed) {
168
+ return backend.shutdown().then(() => {
169
+ throw new Error("AppKit memory closed during Graphiti startup");
170
+ });
171
+ }
172
+ this.graphiti = backend;
173
+ return backend;
174
+ })
175
+ .finally(() => {
176
+ this.graphitiStarting = undefined;
177
+ });
178
+ return this.graphitiStarting;
179
+ }
180
+ actorId(resourceId) {
181
+ if (resourceId)
182
+ return resourceId;
183
+ const context = dbxAppkit.tryGetExecutionContext();
184
+ if (!context)
185
+ throw new Error("Memory tools require an AppKit execution context");
186
+ return "userId" in context ? context.userId : context.serviceUserId;
187
+ }
188
+ requireConfig() {
189
+ if (!this.resolved)
190
+ throw new Error("AppKit memory is not initialized");
191
+ return this.resolved;
192
+ }
193
+ }
194
+ /** AppKit registration factory for {@link MemoryPlugin}. */
195
+ export const memory = toPlugin(MemoryPlugin);
196
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBRUgsT0FBTyxFQUFFLG1CQUFtQixFQUFFLFFBQVEsRUFBdUIsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RixPQUFPLEVBQ0wsVUFBVSxFQUNWLG1CQUFtQixHQUlwQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFDTCxNQUFNLElBQUksU0FBUyxFQUNuQixjQUFjLEVBQ2Qsa0JBQWtCLEdBRW5CLE1BQU0sbUJBQW1CLENBQUM7QUFDM0IsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUV6RCxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLHlCQUF5QixHQU8xQixNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIseUJBQXlCLEdBRzFCLE1BQU0sYUFBYSxDQUFDO0FBQ3JCLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRW5FLGlGQUFpRjtBQUNqRixNQUFNLE9BQU8sWUFBYSxTQUFRLGtCQUFzQztJQUN0RSxNQUFNLENBQUMsS0FBSyxHQUFHLFVBQW1CLENBQUM7SUFDbkMsTUFBTSxDQUFDLFFBQVEsR0FBNkI7UUFDMUMsSUFBSSxFQUFFLFFBQVE7UUFDZCxXQUFXLEVBQUUsUUFBUTtRQUNyQixXQUFXLEVBQUUsZ0ZBQWdGO1FBQzdGLFNBQVMsRUFBRSxNQUFNO1FBQ2pCLFNBQVMsRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRTtRQUN6QyxNQUFNLEVBQUUsRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUU7S0FDekMsQ0FBQztJQUVlLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ25DLFFBQVEsQ0FBOEI7SUFD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Q2xDLE1BQU0sSUFBSSxLQUFLLENBQUMsOENBQThDLENBQUMsQ0FBQztnQkFDbEUsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDO1lBQ0QsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUM7WUFDeEIsT0FBTyxPQUFPLENBQUM7UUFDakIsQ0FBQyxDQUFDO2FBQ0QsT0FBTyxDQUFDLEdBQUcsRUFBRTtZQUNaLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxTQUFTLENBQUM7UUFDcEMsQ0FBQyxDQUFDLENBQUM7UUFDTCxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztJQUMvQixDQUFDO0lBRU8sT0FBTyxDQUFDLFVBQW1CO1FBQ2pDLElBQUksVUFBVTtZQUFFLE9BQU8sVUFBVSxDQUFDO1FBQ2xDLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1FBQ25ELElBQUksQ0FBQyxPQUFPO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxrREFBa0QsQ0FBQyxDQUFDO1FBQ2xGLE9BQU8sUUFBUSxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQztJQUN0RSxDQUFDO0lBRU8sYUFBYTtRQUNuQixJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVE7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7UUFDeEUsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3ZCLENBQUM7O0FBR0gsNERBQTREO0FBQzVELE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsWUFBWSxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEFwcEtpdCBtZW1vcnkgcGx1Z2luIHdpdGggRGF0YWJyaWNrcy1maXJzdCB0b29scyBhbmQgbGF6eSBHcmFwaGl0aSBmYWxsYmFjay5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgZ2V0RXhlY3V0aW9uQ29udGV4dCwgdG9QbHVnaW4sIHR5cGUgUGx1Z2luTWFuaWZlc3QgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQge1xuICBkZWZpbmVUb29sLFxuICBleGVjdXRlRnJvbVJlZ2lzdHJ5LFxuICB0eXBlIFRvb2xraXRFbnRyeSxcbiAgdHlwZSBUb29sa2l0T3B0aW9ucyxcbiAgdHlwZSBUb29sUmVnaXN0cnksXG59IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXQvYmV0YVwiO1xuaW1wb3J0IHtcbiAgYXBwa2l0IGFzIGRieEFwcGtpdCxcbiAgdG9vbGtpdEVudHJpZXMsXG4gIFRvb2xSZWdpc3RyeVBsdWdpbixcbiAgdHlwZSBBZ2VudFRvb2xFeGVjdXRpb25Db250ZXh0LFxufSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB7IGVycm9yVXRpbHMsIGxvZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbmltcG9ydCB7XG4gIFJlY2FsbE1lbW9yeUlucHV0U2NoZW1hLFxuICBSZW1lbWJlck1lbW9yeUlucHV0U2NoZW1hLFxuICB0eXBlIEdyYXBoaXRpTWVtb3J5QmFja2VuZCxcbiAgdHlwZSBNZW1vcnlUdXJuLFxuICB0eXBlIFJlY2FsbE1lbW9yeUlucHV0LFxuICB0eXBlIFJlY2FsbE1lbW9yeVJlc3VsdCxcbiAgdHlwZSBSZW1lbWJlck1lbW9yeUlucHV0LFxuICB0eXBlIFJlbWVtYmVyTWVtb3J5UmVzdWx0LFxufSBmcm9tIFwiLi9fY29udHJhY3RzLnRzXCI7XG5pbXBvcnQge1xuICBNRU1PUllfQ09ORklHX1NDSEVNQSxcbiAgcmVzb2x2ZU1lbW9yeVBsdWdpbkNvbmZpZyxcbiAgdHlwZSBNZW1vcnlQbHVnaW5Db25maWcsXG4gIHR5cGUgUmVzb2x2ZWRNZW1vcnlQbHVnaW5Db25maWcsXG59IGZyb20gXCIuL2NvbmZpZy50c1wiO1xuaW1wb3J0IHsgRGF0YWJyaWNrc01lbW9yeVJ1bnRpbWUgfSBmcm9tIFwiLi9kYXRhYnJpY2tzL19ydW50aW1lLnRzXCI7XG5cbi8qKiBBcHBLaXQgbWVtb3J5IHBsdWdpbiB3aXRoIERhdGFicmlja3MgbWFuYWdlZCBtZW1vcnkgYW5kIEdyYXBoaXRpIGZhbGxiYWNrLiAqL1xuZXhwb3J0IGNsYXNzIE1lbW9yeVBsdWdpbiBleHRlbmRzIFRvb2xSZWdpc3RyeVBsdWdpbjxNZW1vcnlQbHVnaW5Db25maWc+IHtcbiAgc3RhdGljIHBoYXNlID0gXCJkZWZlcnJlZFwiIGFzIGNvbnN0O1xuICBzdGF0aWMgbWFuaWZlc3Q6IFBsdWdpbk1hbmlmZXN0PFwibWVtb3J5XCI+ID0ge1xuICAgIG5hbWU6IFwibWVtb3J5XCIsXG4gICAgZGlzcGxheU5hbWU6IFwiTWVtb3J5XCIsXG4gICAgZGVzY3JpcHRpb246IFwiUHVibGlzaGVzIERhdGFicmlja3MtZmlyc3QgZHVyYWJsZSBtZW1vcnkgdG9vbHMgd2l0aCBhIGxhenkgR3JhcGhpdGkgZmFsbGJhY2suXCIsXG4gICAgc3RhYmlsaXR5OiBcImJldGFcIixcbiAgICByZXNvdXJjZXM6IHsgcmVxdWlyZWQ6IFtdLCBvcHRpb25hbDogW10gfSxcbiAgICBjb25maWc6IHsgc2NoZW1hOiBNRU1PUllfQ09ORklHX1NDSEVNQSB9LFxuICB9O1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgbG9nZ2VyID0gbG9nLmxvZ2dlcih0aGlzKTtcbiAgcHJpdmF0ZSByZXNvbHZlZD86IFJlc29sdmVkTWVtb3J5UGx1Z2luQ29uZmlnO1xuICBwcml2YXRlIGRhdGFicmlja3M/OiBEYXRhYnJpY2tzTWVtb3J5UnVudGltZTtcbiAgcHJpdmF0ZSBncmFwaGl0aT86IEdyYXBoaXRpTWVtb3J5QmFja2VuZDtcbiAgcHJpdmF0ZSBncmFwaGl0aVN0YXJ0aW5nPzogUHJvbWlzZTxHcmFwaGl0aU1lbW9yeUJhY2tlbmQ+O1xuICBwcml2YXRlIHJlYWRvbmx5IGdyYXBoaXRpU3RhcnR1cEFib3J0ID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBwcml2YXRlIGNsb3NlZCA9IGZhbHNlO1xuXG4gIHByb3RlY3RlZCBnZXQgdG9vbFJlZ2lzdHJ5KCk6IFRvb2xSZWdpc3RyeSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaXN0cnkoKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGFzeW5jIHNldHVwKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZU1lbW9yeVBsdWdpbkNvbmZpZyh0aGlzLmNvbmZpZyk7XG4gICAgaWYgKHJlc29sdmVkLnNhdmVUdXJucyA9PT0gdHJ1ZSAmJiByZXNvbHZlZC5ncmFwaGl0aSA9PT0gZmFsc2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkFwcEtpdCBtZW1vcnkgc2F2ZVR1cm5zIHJlcXVpcmVzIHRoZSBHcmFwaGl0aSBmYWxsYmFja1wiKTtcbiAgICB9XG4gICAgdGhpcy5yZXNvbHZlZCA9IHJlc29sdmVkO1xuICAgIGlmIChyZXNvbHZlZC5kYXRhYnJpY2tzKSB7XG4gICAgICB0aGlzLmRhdGFicmlja3MgPSBuZXcgRGF0YWJyaWNrc01lbW9yeVJ1bnRpbWUoXG4gICAgICAgIGdldEV4ZWN1dGlvbkNvbnRleHQoKS5jbGllbnQsXG4gICAgICAgIHJlc29sdmVkLmRhdGFicmlja3MsXG4gICAgICApO1xuICAgIH1cblxuICAgIGxldCBtYXN0cmEgPSBmYWxzZTtcbiAgICBjb25zdCBjb250ZXh0ID0gdGhpcy5jb250ZXh0O1xuICAgIGNvbnN0IHNob3VsZEF0dGFjaFR1cm5TYXZlciA9XG4gICAgICByZXNvbHZlZC5zYXZlVHVybnMgIT09IGZhbHNlICYmXG4gICAgICByZXNvbHZlZC5ncmFwaGl0aSAhPT0gZmFsc2UgJiZcbiAgICAgIGNvbnRleHQ/Lmhhc1BsdWdpbihcIm1hc3RyYVwiKSA9PT0gdHJ1ZTtcbiAgICBpZiAoc2hvdWxkQXR0YWNoVHVyblNhdmVyICYmIGNvbnRleHQpIHtcbiAgICAgIGNvbnN0IHsgYXR0YWNoTWFzdHJhVHVyblNhdmVyIH0gPSBhd2FpdCBpbXBvcnQoXCIuL19tYXN0cmEudHNcIik7XG4gICAgICBtYXN0cmEgPSBhd2FpdCBhdHRhY2hNYXN0cmFUdXJuU2F2ZXIoY29udGV4dCwgKHR1cm4sIHNpZ25hbCkgPT4gdGhpcy5zYXZlVHVybih0dXJuLCBzaWduYWwpKTtcbiAgICB9XG4gICAgaWYgKHJlc29sdmVkLnNhdmVUdXJucyA9PT0gdHJ1ZSAmJiAhbWFzdHJhKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBcHBLaXQgbWVtb3J5IHNhdmVUdXJucyByZXF1aXJlcyBhIHJlZ2lzdGVyZWQgTWFzdHJhIHBsdWdpblwiKTtcbiAgICB9XG4gICAgdGhpcy5sb2dnZXIuaW5mbyhcInJlYWR5XCIsIHtcbiAgICAgIGRhdGFicmlja3M6IHJlc29sdmVkLmRhdGFicmlja3MgIT09IGZhbHNlLFxuICAgICAgZ3JhcGhpdGk6IHJlc29sdmVkLmdyYXBoaXRpICE9PSBmYWxzZSA/IFwibGF6eVwiIDogZmFsc2UsXG4gICAgICBtYXN0cmFUdXJuU2F2ZXI6IG1hc3RyYSxcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIHRvb2xraXQob3B0aW9ucz86IFRvb2xraXRPcHRpb25zKTogUHJvbWlzZTxSZWNvcmQ8c3RyaW5nLCBUb29sa2l0RW50cnk+PiB7XG4gICAgcmV0dXJuIHRvb2xraXRFbnRyaWVzLmVudHJpZXMoXCJtZW1vcnlcIiwgdGhpcy5nZXRBZ2VudFRvb2xzKCksIHtcbiAgICAgIC4uLm9wdGlvbnMsXG4gICAgICBwcmVmaXg6IG9wdGlvbnM/LnByZWZpeCA/PyBcIlwiLFxuICAgIH0pO1xuICB9XG5cbiAgb3ZlcnJpZGUgZXhlY3V0ZUFnZW50VG9vbChcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgYXJnczogdW5rbm93bixcbiAgICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbiAgICBjb250ZXh0PzogQWdlbnRUb29sRXhlY3V0aW9uQ29udGV4dCxcbiAgKTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIGV4ZWN1dGVGcm9tUmVnaXN0cnkodGhpcy5yZWdpc3RyeShjb250ZXh0Py5yZXNvdXJjZUlkKSwgbmFtZSwgYXJncywgc2lnbmFsKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGFib3J0QWN0aXZlT3BlcmF0aW9ucygpOiB2b2lkIHtcbiAgICBzdXBlci5hYm9ydEFjdGl2ZU9wZXJhdGlvbnMoKTtcbiAgICB0aGlzLmdyYXBoaXRpU3RhcnR1cEFib3J0LmFib3J0KG5ldyBFcnJvcihcIkFwcEtpdCBtZW1vcnkgaXMgc2h1dHRpbmcgZG93blwiKSk7XG4gIH1cblxuICBhc3luYyBzaHV0ZG93bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNsb3NlZCA9IHRydWU7XG4gICAgdGhpcy5ncmFwaGl0aVN0YXJ0dXBBYm9ydC5hYm9ydChuZXcgRXJyb3IoXCJBcHBLaXQgbWVtb3J5IGlzIHNodXR0aW5nIGRvd25cIikpO1xuICAgIGNvbnN0IHN0YXJ0aW5nID0gdGhpcy5ncmFwaGl0aVN0YXJ0aW5nO1xuICAgIGNvbnN0IGJhY2tlbmQgPSB0aGlzLmdyYXBoaXRpO1xuICAgIHRoaXMuZ3JhcGhpdGlTdGFydGluZyA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmdyYXBoaXRpID0gdW5kZWZpbmVkO1xuICAgIGF3YWl0IHN0YXJ0aW5nPy5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICAgIGF3YWl0IGJhY2tlbmQ/LnNodXRkb3duKCk7XG4gIH1cblxuICBwcml2YXRlIHJlZ2lzdHJ5KHJlc291cmNlSWQ/OiBzdHJpbmcpOiBUb29sUmVnaXN0cnkge1xuICAgIHJldHVybiB7XG4gICAgICByZW1lbWJlcjogZGVmaW5lVG9vbCh7XG4gICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgIFwiU2F2ZSBhIGR1cmFibGUgdXNlciBwcmVmZXJlbmNlLCBmYWN0LCBkZWNpc2lvbiwgb3IgaW5zdHJ1Y3Rpb24gZm9yIGZ1dHVyZSBjb252ZXJzYXRpb25zLlwiLFxuICAgICAgICBzY2hlbWE6IFJlbWVtYmVyTWVtb3J5SW5wdXRTY2hlbWEsXG4gICAgICAgIGFubm90YXRpb25zOiB7IGVmZmVjdDogXCJ3cml0ZVwiLCByZXF1aXJlc1VzZXJDb250ZXh0OiB0cnVlIH0sXG4gICAgICAgIGF1dG9Jbmhlcml0YWJsZTogZmFsc2UsXG4gICAgICAgIGV4ZWN1dGU6IChpbnB1dCwgc2lnbmFsKSA9PiB0aGlzLnJlbWVtYmVyKHRoaXMuYWN0b3JJZChyZXNvdXJjZUlkKSwgaW5wdXQsIHNpZ25hbCksXG4gICAgICB9KSxcbiAgICAgIHJlY2FsbF9tZW1vcnk6IGRlZmluZVRvb2woe1xuICAgICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgICBcIlNlYXJjaCBkdXJhYmxlIGFjdG9yLXNjb3BlZCBtZW1vcnkgZm9yIGZhY3RzIGFuZCBwcmVmZXJlbmNlcyByZWxldmFudCB0byB0aGUgY3VycmVudCB0YXNrLlwiLFxuICAgICAgICBzY2hlbWE6IFJlY2FsbE1lbW9yeUlucHV0U2NoZW1hLFxuICAgICAgICBhbm5vdGF0aW9uczogeyBlZmZlY3Q6IFwicmVhZFwiLCByZXF1aXJlc1VzZXJDb250ZXh0OiB0cnVlIH0sXG4gICAgICAgIGF1dG9Jbmhlcml0YWJsZTogZmFsc2UsXG4gICAgICAgIGV4ZWN1dGU6IChpbnB1dCwgc2lnbmFsKSA9PiB0aGlzLnJlY2FsbCh0aGlzLmFjdG9ySWQocmVzb3VyY2VJZCksIGlucHV0LCBzaWduYWwpLFxuICAgICAgfSksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVtZW1iZXIoXG4gICAgYWN0b3JJZDogc3RyaW5nLFxuICAgIGlucHV0OiBSZW1lbWJlck1lbW9yeUlucHV0LFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPFJlbWVtYmVyTWVtb3J5UmVzdWx0PiB7XG4gICAgY29uc3QgZGF0YWJyaWNrcyA9IGF3YWl0IHRoaXMuZGF0YWJyaWNrc0JhY2tlbmQoYWN0b3JJZCwgc2lnbmFsKTtcbiAgICBpZiAoZGF0YWJyaWNrcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGRhdGFicmlja3MucmVtZW1iZXIoYWN0b3JJZCwgaW5wdXQsIHNpZ25hbCk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoIXRoaXMuc2hvdWxkRmFsbGJhY2tGcm9tRGF0YWJyaWNrcyhlcnJvcikpIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gKGF3YWl0IHRoaXMuZ3JhcGhpdGlCYWNrZW5kKCkpLnJlbWVtYmVyKGFjdG9ySWQsIGlucHV0LCBzaWduYWwpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWNhbGwoXG4gICAgYWN0b3JJZDogc3RyaW5nLFxuICAgIGlucHV0OiBSZWNhbGxNZW1vcnlJbnB1dCxcbiAgICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbiAgKTogUHJvbWlzZTxSZWNhbGxNZW1vcnlSZXN1bHQ+IHtcbiAgICBjb25zdCBwYXJzZWQgPSBSZWNhbGxNZW1vcnlJbnB1dFNjaGVtYS5wYXJzZShpbnB1dCk7XG4gICAgY29uc3QgZGF0YWJyaWNrcyA9IGF3YWl0IHRoaXMuZGF0YWJyaWNrc0JhY2tlbmQoYWN0b3JJZCwgc2lnbmFsKTtcbiAgICBpZiAoZGF0YWJyaWNrcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGRhdGFicmlja3MucmVjYWxsKGFjdG9ySWQsIHBhcnNlZCwgc2lnbmFsKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGlmICghdGhpcy5zaG91bGRGYWxsYmFja0Zyb21EYXRhYnJpY2tzKGVycm9yKSkgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiAoYXdhaXQgdGhpcy5ncmFwaGl0aUJhY2tlbmQoKSkucmVjYWxsKGFjdG9ySWQsIHBhcnNlZCwgc2lnbmFsKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc2F2ZVR1cm4odHVybjogTWVtb3J5VHVybiwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoYXdhaXQgdGhpcy5kYXRhYnJpY2tzQmFja2VuZCh0dXJuLmFjdG9ySWQsIHNpZ25hbCkpIHJldHVybjtcbiAgICBhd2FpdCAoYXdhaXQgdGhpcy5ncmFwaGl0aUJhY2tlbmQoKSkuc2F2ZVR1cm4odHVybiwgc2lnbmFsKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZGF0YWJyaWNrc0JhY2tlbmQoXG4gICAgYWN0b3JJZDogc3RyaW5nLFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPERhdGFicmlja3NNZW1vcnlSdW50aW1lIHwgdW5kZWZpbmVkPiB7XG4gICAgY29uc3QgYmFja2VuZCA9IHRoaXMuZGF0YWJyaWNrcztcbiAgICBpZiAoIWJhY2tlbmQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgaWYgKGF3YWl0IGJhY2tlbmQuYXZhaWxhYmxlKGFjdG9ySWQsIHNpZ25hbCkpIHJldHVybiBiYWNrZW5kO1xuICAgIGNvbnN0IGNvbmZpZ3VyZWQgPSB0aGlzLnJlcXVpcmVDb25maWcoKS5kYXRhYnJpY2tzO1xuICAgIGlmIChjb25maWd1cmVkICYmIGNvbmZpZ3VyZWQubW9kZSA9PT0gXCJyZXF1aXJlZFwiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJSZXF1aXJlZCBEYXRhYnJpY2tzIG1hbmFnZWQgbWVtb3J5IGlzIHVuYXZhaWxhYmxlIGZvciB0aGUgY3VycmVudCBhY3RvclwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHByaXZhdGUgc2hvdWxkRmFsbGJhY2tGcm9tRGF0YWJyaWNrcyhlcnJvcjogdW5rbm93bik6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGNvbmZpZ3VyZWQgPSB0aGlzLnJlcXVpcmVDb25maWcoKS5kYXRhYnJpY2tzO1xuICAgIHJldHVybiAoXG4gICAgICBjb25maWd1cmVkICE9PSBmYWxzZSAmJlxuICAgICAgY29uZmlndXJlZC5tb2RlID09PSBcImF1dG9cIiAmJlxuICAgICAgZXJyb3JVdGlscy5lcnJvckNvbnRleHQoZXJyb3IpLmhhc1N0YXR1c0NvZGUoNDAzLCA0MDQpXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ3JhcGhpdGlCYWNrZW5kKCk6IFByb21pc2U8R3JhcGhpdGlNZW1vcnlCYWNrZW5kPiB7XG4gICAgaWYgKHRoaXMuY2xvc2VkKSB0aHJvdyBuZXcgRXJyb3IoXCJBcHBLaXQgbWVtb3J5IGlzIGNsb3NlZFwiKTtcbiAgICBpZiAodGhpcy5ncmFwaGl0aT8ucnVubmluZykgcmV0dXJuIHRoaXMuZ3JhcGhpdGk7XG4gICAgaWYgKHRoaXMuZ3JhcGhpdGkpIHtcbiAgICAgIGF3YWl0IHRoaXMuZ3JhcGhpdGkuc2h1dGRvd24oKTtcbiAgICAgIHRoaXMuZ3JhcGhpdGkgPSB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGNvbnN0IG9wdGlvbnMgPSB0aGlzLnJlcXVpcmVDb25maWcoKS5ncmFwaGl0aTtcbiAgICBpZiAoIW9wdGlvbnMpIHRocm93IG5ldyBFcnJvcihcIkdyYXBoaXRpIGZhbGxiYWNrIGlzIGRpc2FibGVkXCIpO1xuICAgIHRoaXMuZ3JhcGhpdGlTdGFydGluZyA/Pz0gaW1wb3J0KFwiLi9ncmFwaGl0aS9fYmFja2VuZC50c1wiKVxuICAgICAgLnRoZW4oKHsgc3RhcnRHcmFwaGl0aUJhY2tlbmQgfSkgPT5cbiAgICAgICAgc3RhcnRHcmFwaGl0aUJhY2tlbmQob3B0aW9ucywgdGhpcy5ncmFwaGl0aVN0YXJ0dXBBYm9ydC5zaWduYWwpLFxuICAgICAgKVxuICAgICAgLnRoZW4oKGJhY2tlbmQpID0+IHtcbiAgICAgICAgaWYgKHRoaXMuY2xvc2VkKSB7XG4gICAgICAgICAgcmV0dXJuIGJhY2tlbmQuc2h1dGRvd24oKS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkFwcEtpdCBtZW1vcnkgY2xvc2VkIGR1cmluZyBHcmFwaGl0aSBzdGFydHVwXCIpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZ3JhcGhpdGkgPSBiYWNrZW5kO1xuICAgICAgICByZXR1cm4gYmFja2VuZDtcbiAgICAgIH0pXG4gICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIHRoaXMuZ3JhcGhpdGlTdGFydGluZyA9IHVuZGVmaW5lZDtcbiAgICAgIH0pO1xuICAgIHJldHVybiB0aGlzLmdyYXBoaXRpU3RhcnRpbmc7XG4gIH1cblxuICBwcml2YXRlIGFjdG9ySWQocmVzb3VyY2VJZD86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKHJlc291cmNlSWQpIHJldHVybiByZXNvdXJjZUlkO1xuICAgIGNvbnN0IGNvbnRleHQgPSBkYnhBcHBraXQudHJ5R2V0RXhlY3V0aW9uQ29udGV4dCgpO1xuICAgIGlmICghY29udGV4dCkgdGhyb3cgbmV3IEVycm9yKFwiTWVtb3J5IHRvb2xzIHJlcXVpcmUgYW4gQXBwS2l0IGV4ZWN1dGlvbiBjb250ZXh0XCIpO1xuICAgIHJldHVybiBcInVzZXJJZFwiIGluIGNvbnRleHQgPyBjb250ZXh0LnVzZXJJZCA6IGNvbnRleHQuc2VydmljZVVzZXJJZDtcbiAgfVxuXG4gIHByaXZhdGUgcmVxdWlyZUNvbmZpZygpOiBSZXNvbHZlZE1lbW9yeVBsdWdpbkNvbmZpZyB7XG4gICAgaWYgKCF0aGlzLnJlc29sdmVkKSB0aHJvdyBuZXcgRXJyb3IoXCJBcHBLaXQgbWVtb3J5IGlzIG5vdCBpbml0aWFsaXplZFwiKTtcbiAgICByZXR1cm4gdGhpcy5yZXNvbHZlZDtcbiAgfVxufVxuXG4vKiogQXBwS2l0IHJlZ2lzdHJhdGlvbiBmYWN0b3J5IGZvciB7QGxpbmsgTWVtb3J5UGx1Z2lufS4gKi9cbmV4cG9ydCBjb25zdCBtZW1vcnkgPSB0b1BsdWdpbihNZW1vcnlQbHVnaW4pO1xuIl19
@@ -0,0 +1 @@
1
+ {"root":["../src/_contracts.ts","../src/_mastra.ts","../src/config.ts","../src/plugin.ts","../src/databricks/_runtime.ts","../src/databricks/client.ts","../src/databricks/index.ts","../src/graphiti/_backend.ts","../src/graphiti/index.ts","../src/graphiti/options.ts","../src/graphiti/runtime.ts","../smoke/mastra-tool.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -1,6 +1,106 @@
1
1
  {
2
2
  "name": "@dbx-tools/appkit-memory",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "AppKit durable memory tools with Databricks managed memory and lazy Graphiti fallback",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/reggie-db/dbx-tools.git",
7
+ "directory": "packages/js/node/appkit-memory"
8
+ },
9
+ "scripts": {
10
+ "build": "projen build",
11
+ "compile": "projen compile",
12
+ "default": "projen default",
13
+ "package": "projen package",
14
+ "post-compile": "projen post-compile",
15
+ "pre-compile": "projen pre-compile",
16
+ "prepack": "projen prepack",
17
+ "test": "projen test",
18
+ "watch": "projen watch",
19
+ "projen": "projen"
20
+ },
21
+ "devDependencies": {
22
+ "@dbx-tools/appkit-mastra": "^0.9.131",
23
+ "@types/json-schema": "^7",
24
+ "@types/node": "^24.6.0",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "3.2.4"
27
+ },
28
+ "peerDependencies": {
29
+ "@dbx-tools/appkit-mastra": "^0.9.131"
30
+ },
31
+ "dependencies": {
32
+ "@databricks/appkit": "0.81.0",
33
+ "@dbx-tools/appkit": "^0.9.131",
34
+ "@dbx-tools/auth": "^0.9.131",
35
+ "@dbx-tools/cache": "^0.9.131",
36
+ "@dbx-tools/core": "^0.9.131",
37
+ "@dbx-tools/shared-core": "^0.9.131",
38
+ "@dbx-tools/shared-graphiti": "^0.9.131",
39
+ "semver": "^7.7.3",
40
+ "zod": "4.3.6"
41
+ },
42
+ "main": "./lib/index.js",
43
+ "license": "Apache-2.0",
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "main": "./lib/index.js",
47
+ "types": "./lib/index.d.ts",
48
+ "exports": {
49
+ ".": {
50
+ "types": "./lib/index.d.ts",
51
+ "default": "./lib/index.js"
52
+ },
53
+ "./config": {
54
+ "types": "./lib/src/config.d.ts",
55
+ "default": "./lib/src/config.js"
56
+ },
57
+ "./plugin": {
58
+ "types": "./lib/src/plugin.d.ts",
59
+ "default": "./lib/src/plugin.js"
60
+ },
61
+ "./*": {
62
+ "types": "./lib/src/*/index.d.ts",
63
+ "default": "./lib/src/*/index.js"
64
+ },
65
+ "./package.json": "./package.json"
66
+ }
67
+ },
68
+ "version": "0.9.131",
69
+ "types": "./lib/index.d.ts",
70
+ "dbxToolsConfig": {
71
+ "tags": [
72
+ "node"
73
+ ]
74
+ },
75
+ "type": "module",
76
+ "exports": {
77
+ ".": {
78
+ "types": "./lib/index.d.ts",
79
+ "default": "./lib/index.js"
80
+ },
81
+ "./config": {
82
+ "types": "./lib/src/config.d.ts",
83
+ "default": "./lib/src/config.js"
84
+ },
85
+ "./plugin": {
86
+ "types": "./lib/src/plugin.d.ts",
87
+ "default": "./lib/src/plugin.js"
88
+ },
89
+ "./*": {
90
+ "types": "./lib/src/*/index.d.ts",
91
+ "default": "./lib/src/*/index.js"
92
+ },
93
+ "./package.json": "./package.json"
94
+ },
95
+ "files": [
96
+ "index.ts",
97
+ "src",
98
+ "lib"
99
+ ],
100
+ "peerDependenciesMeta": {
101
+ "@dbx-tools/appkit-mastra": {
102
+ "optional": true
103
+ }
104
+ },
105
+ "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"bunx projen\"."
106
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Backend-neutral memory tool and turn contracts.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { z } from "zod";
8
+
9
+ export const RememberMemoryInputSchema = z
10
+ .object({
11
+ path: z
12
+ .string()
13
+ .trim()
14
+ .startsWith("/")
15
+ .describe("Stable absolute memory path, such as /preferences/response.md."),
16
+ content: z.string().trim().min(1).describe("Durable memory content."),
17
+ description: z
18
+ .string()
19
+ .trim()
20
+ .min(1)
21
+ .optional()
22
+ .describe("Short retrieval-oriented description."),
23
+ })
24
+ .strict()
25
+ .describe("Durable memory to save for the current actor.");
26
+
27
+ export type RememberMemoryInput = z.infer<typeof RememberMemoryInputSchema>;
28
+
29
+ export const RecallMemoryInputSchema = z
30
+ .object({
31
+ query: z.string().trim().min(1).describe("Question or phrase used to retrieve memory."),
32
+ limit: z.coerce
33
+ .number<number>()
34
+ .int()
35
+ .min(1)
36
+ .max(100)
37
+ .default(10)
38
+ .describe("Maximum number of matching memories to return."),
39
+ })
40
+ .strict()
41
+ .describe("Actor-scoped memory search.");
42
+
43
+ export type RecallMemoryInput = z.input<typeof RecallMemoryInputSchema>;
44
+
45
+ export const MemoryBackendSchema = z
46
+ .enum(["databricks", "graphiti"])
47
+ .describe("Memory backend that completed the operation.");
48
+
49
+ export type MemoryBackend = z.infer<typeof MemoryBackendSchema>;
50
+
51
+ export const MemoryRecordSchema = z
52
+ .object({
53
+ path: z.string().optional().describe("Stable memory path when the backend supports paths."),
54
+ content: z.string().describe("Retrieved or saved memory content."),
55
+ description: z.string().optional().describe("Retrieval-oriented memory description."),
56
+ score: z.number().optional().describe("Backend relevance score when available."),
57
+ })
58
+ .strict()
59
+ .describe("Backend-neutral durable memory record.");
60
+
61
+ export type MemoryRecord = z.infer<typeof MemoryRecordSchema>;
62
+
63
+ export const RememberMemoryResultSchema = z
64
+ .object({
65
+ backend: MemoryBackendSchema,
66
+ memory: MemoryRecordSchema,
67
+ })
68
+ .strict()
69
+ .describe("Result of saving one durable memory.");
70
+
71
+ export type RememberMemoryResult = z.infer<typeof RememberMemoryResultSchema>;
72
+
73
+ export const RecallMemoryResultSchema = z
74
+ .object({
75
+ backend: MemoryBackendSchema,
76
+ memories: z.array(MemoryRecordSchema).describe("Matching actor-scoped memories."),
77
+ })
78
+ .strict()
79
+ .describe("Result of one durable memory search.");
80
+
81
+ export type RecallMemoryResult = z.infer<typeof RecallMemoryResultSchema>;
82
+
83
+ /** Completed Mastra turn offered to the optional external saver. */
84
+ export interface MemoryTurn {
85
+ readonly agentId: string;
86
+ readonly actorId: string;
87
+ readonly threadId: string;
88
+ readonly user: string;
89
+ readonly assistant: string;
90
+ }
91
+
92
+ /** Lazy Graphiti backend used only after managed memory is unavailable. */
93
+ export interface GraphitiMemoryBackend {
94
+ readonly running: boolean;
95
+ remember(
96
+ actorId: string,
97
+ input: RememberMemoryInput,
98
+ signal?: AbortSignal,
99
+ ): Promise<RememberMemoryResult>;
100
+ recall(
101
+ actorId: string,
102
+ input: z.output<typeof RecallMemoryInputSchema>,
103
+ signal?: AbortSignal,
104
+ ): Promise<RecallMemoryResult>;
105
+ saveTurn(turn: MemoryTurn, signal?: AbortSignal): Promise<void>;
106
+ shutdown(): Promise<void>;
107
+ }
package/src/_mastra.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Optional Mastra output-processor attachment.
3
+ *
4
+ * Loaded only when the memory plugin finds a registered Mastra sibling.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ import { pluginRegistry } from "@dbx-tools/appkit";
10
+ import { errorUtils, log, object } from "@dbx-tools/shared-core";
11
+
12
+ import type { MemoryTurn } from "./_contracts.ts";
13
+
14
+ const logger = log.logger("memory/mastra");
15
+
16
+ interface MemoryMessage {
17
+ readonly role: string;
18
+ readonly content: { readonly parts: readonly unknown[] };
19
+ }
20
+
21
+ /** Attach a memory-owned turn saver through Mastra's generic processor extension. */
22
+ export async function attachMastraTurnSaver(
23
+ context: pluginRegistry.PluginContextLike,
24
+ saveTurn: (turn: MemoryTurn, signal?: AbortSignal) => Promise<void>,
25
+ ): Promise<boolean> {
26
+ const mastraModule = await import("@dbx-tools/appkit-mastra");
27
+ const plugin = pluginRegistry.instance(context, mastraModule.mastra);
28
+ if (!plugin) return false;
29
+ plugin.exports().registerOutputProcessor((agentId) => ({
30
+ id: `appkit_memory_${agentId}`,
31
+ name: "AppKit memory turn saver",
32
+ description: "Saves completed turns to the configured fallback memory backend.",
33
+ processOutputResult: async ({ messageList, requestContext, abortSignal }) => {
34
+ const scope = mastraModule.memory.requestMemoryScope(requestContext);
35
+ if (!scope) return messageList;
36
+ const actorId = typeof scope.resource === "string" ? scope.resource : undefined;
37
+ const threadId = typeof scope.thread === "string" ? scope.thread : undefined;
38
+ if (!actorId || !threadId) return messageList;
39
+ const user = latestText(messageList.get.input.db(), "user");
40
+ const assistant = latestText(messageList.get.response.db(), "assistant");
41
+ if (!user || !assistant) return messageList;
42
+ try {
43
+ await saveTurn(
44
+ {
45
+ agentId,
46
+ actorId,
47
+ threadId,
48
+ user,
49
+ assistant,
50
+ },
51
+ abortSignal,
52
+ );
53
+ } catch (error) {
54
+ logger.warn("turn save failed", { error: errorUtils.toError(error), agentId });
55
+ }
56
+ return messageList;
57
+ },
58
+ }));
59
+ return true;
60
+ }
61
+
62
+ function latestText(
63
+ messages: readonly MemoryMessage[],
64
+ role: "user" | "assistant",
65
+ ): string | undefined {
66
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
67
+ const message = messages[index];
68
+ if (message?.role !== role) continue;
69
+ const text = message.content.parts
70
+ .flatMap((part) =>
71
+ object.isRecord(part) && part.type === "text" && typeof part.text === "string"
72
+ ? [part.text]
73
+ : [],
74
+ )
75
+ .join("\n")
76
+ .trim();
77
+ if (text) return text;
78
+ }
79
+ return undefined;
80
+ }
package/src/config.ts ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Top-level AppKit memory plugin configuration.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import type { BasePluginConfig } from "@databricks/appkit";
8
+ import { projectUtils } from "@dbx-tools/core";
9
+ import { stringUtils } from "@dbx-tools/shared-core";
10
+ import {
11
+ GraphitiOptionsSchema,
12
+ graphitiOptionOverrides,
13
+ type GraphitiOptions,
14
+ } from "@dbx-tools/shared-graphiti";
15
+ import type { JSONSchema7 } from "json-schema";
16
+ import { z } from "zod";
17
+
18
+ export const DatabricksMemoryOptionsSchema = z
19
+ .object({
20
+ memoryStore: z
21
+ .string()
22
+ .trim()
23
+ .min(1)
24
+ .optional()
25
+ .describe("Existing Databricks Agent Memory Store id or resource name."),
26
+ mode: z
27
+ .enum(["auto", "required"])
28
+ .default("auto")
29
+ .describe("Whether an inaccessible managed-memory store falls back or fails."),
30
+ })
31
+ .strict()
32
+ .describe("Databricks managed-memory backend configuration.");
33
+
34
+ export type DatabricksMemoryOptions = z.input<typeof DatabricksMemoryOptionsSchema>;
35
+
36
+ const GraphitiMemoryOptionsSchema = GraphitiOptionsSchema.partial().describe(
37
+ "Graphiti option overrides for the lazy fallback.",
38
+ );
39
+
40
+ export const MemoryPluginOptionsSchema = z
41
+ .object({
42
+ databricks: z
43
+ .union([z.literal("auto"), z.boolean(), DatabricksMemoryOptionsSchema])
44
+ .default("auto")
45
+ .describe(
46
+ "Databricks managed memory mode: auto falls back, true requires access, and false disables it.",
47
+ ),
48
+ graphiti: z
49
+ .union([z.boolean(), GraphitiMemoryOptionsSchema])
50
+ .default(true)
51
+ .describe("Lazy Graphiti fallback configuration, enabled by default."),
52
+ saveTurns: z
53
+ .union([z.literal("auto"), z.boolean()])
54
+ .default("auto")
55
+ .describe(
56
+ "Completed-turn saving mode: auto attaches when possible, true requires attachment, and false disables it.",
57
+ ),
58
+ })
59
+ .strict()
60
+ .describe("AppKit memory plugin options.");
61
+
62
+ export type MemoryPluginOptions = z.input<typeof MemoryPluginOptionsSchema>;
63
+
64
+ /** AppKit base configuration plus the memory plugin's serialized options. */
65
+ export type MemoryPluginConfig = BasePluginConfig & MemoryPluginOptions;
66
+
67
+ /** Resolved Databricks store identity and availability policy. */
68
+ export interface ResolvedDatabricksMemoryOptions {
69
+ readonly memoryStore: string;
70
+ readonly mode: "auto" | "required";
71
+ }
72
+
73
+ /** Fully resolved top-level memory configuration. */
74
+ export interface ResolvedMemoryPluginConfig {
75
+ readonly databricks: false | ResolvedDatabricksMemoryOptions;
76
+ readonly graphiti: false | GraphitiOptions;
77
+ readonly saveTurns: "auto" | boolean;
78
+ }
79
+
80
+ /** JSON Schema published through the AppKit memory plugin manifest. */
81
+ export const MEMORY_CONFIG_SCHEMA = z.toJSONSchema(MemoryPluginOptionsSchema) as JSONSchema7;
82
+
83
+ /** Resolve plugin defaults without parsing unrelated AppKit base fields. */
84
+ export function resolveMemoryPluginConfig(
85
+ config: MemoryPluginConfig = {},
86
+ ): ResolvedMemoryPluginConfig {
87
+ const parsed = MemoryPluginOptionsSchema.parse({
88
+ databricks: config.databricks,
89
+ graphiti: config.graphiti,
90
+ saveTurns: config.saveTurns,
91
+ });
92
+ if (parsed.databricks === false && parsed.graphiti === false) {
93
+ throw new Error("AppKit memory requires at least one enabled backend");
94
+ }
95
+ return {
96
+ databricks:
97
+ parsed.databricks === false
98
+ ? false
99
+ : {
100
+ memoryStore:
101
+ typeof parsed.databricks === "object" && parsed.databricks.memoryStore
102
+ ? resourceId(parsed.databricks.memoryStore, "memory-stores")
103
+ : defaultMemoryStore(),
104
+ mode:
105
+ parsed.databricks === true
106
+ ? "required"
107
+ : typeof parsed.databricks === "object"
108
+ ? parsed.databricks.mode
109
+ : "auto",
110
+ },
111
+ graphiti:
112
+ config.graphiti === false
113
+ ? false
114
+ : config.graphiti === undefined || config.graphiti === true
115
+ ? {}
116
+ : graphitiOptionOverrides(config.graphiti),
117
+ saveTurns: parsed.saveTurns,
118
+ };
119
+ }
120
+
121
+ function defaultMemoryStore(): string {
122
+ const slug = stringUtils.toSlugWithOptions(
123
+ { maxLength: 56, truncateStrategy: "hash" },
124
+ projectUtils.name(),
125
+ );
126
+ const id = /^[a-z]/.test(slug) ? slug : `app-${slug}`;
127
+ return (id.length < 3 ? `${id}-store` : id).slice(0, 56);
128
+ }
129
+
130
+ function resourceId(value: string, prefix: string): string {
131
+ const trimmed = value.trim().replace(new RegExp(`^${prefix}/`), "");
132
+ if (!trimmed) throw new Error(`${prefix} id is required`);
133
+ return trimmed;
134
+ }