@cydm/happy-elves 0.1.0-beta.73 → 0.1.0-beta.74
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/apps/daemon/dist/gateway/runtime.js +1 -0
- package/apps/daemon/dist/lifecycle/detached-helper-bundle.mjs +53 -4
- package/apps/daemon/dist/loop/runner.js +1 -0
- package/apps/daemon/dist/memory/commands.js +44 -42
- package/apps/daemon/dist/memory/flush.js +1 -0
- package/apps/daemon/dist/memory/inject.d.ts +1 -0
- package/apps/daemon/dist/memory/inject.js +6 -4
- package/apps/daemon/dist/memory/managers.d.ts +5 -3
- package/apps/daemon/dist/memory/managers.js +110 -34
- package/apps/daemon/dist/memory/project-commands.d.ts +3 -0
- package/apps/daemon/dist/memory/project-commands.js +394 -0
- package/apps/daemon/dist/relay/connection.js +5 -0
- package/apps/daemon/dist/relay/register.js +1 -0
- package/apps/daemon/dist/runtime/external-provider.js +40 -3
- package/apps/daemon/dist/session/prompt.js +1 -0
- package/apps/daemon/package.json +1 -1
- package/apps/relay/dist/controller-handlers.js +132 -0
- package/apps/relay/dist/db.js +67 -1
- package/apps/relay/dist/http-routes.js +1 -1
- package/apps/relay/dist/http-schemas.d.ts +1 -0
- package/apps/relay/dist/http-schemas.js +1 -1
- package/apps/relay/dist/machine-command-result-handlers.js +55 -1
- package/apps/relay/dist/projections.js +2 -0
- package/apps/relay/dist/relay-context.js +5 -0
- package/apps/relay/dist/scope.js +5 -0
- package/apps/relay/dist/types.d.ts +2 -1
- package/apps/relay/dist/websocket.js +2 -0
- package/build-identity.json +2 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/packages/client/dist/client.d.ts +19 -2
- package/packages/client/dist/client.js +289 -3
- package/packages/client/dist/index.d.ts +2 -2
- package/packages/client/dist/live-types.d.ts +1 -1
- package/packages/client/dist/transport.d.ts +5 -0
- package/packages/client/dist/transport.js +19 -1
- package/packages/client/dist/types.d.ts +20 -0
- package/packages/daemon-service/dist/index.js +14 -2
- package/packages/memory/dist/index.d.ts +2 -1
- package/packages/memory/dist/index.js +2 -1
- package/packages/memory/dist/manager.d.ts +18 -0
- package/packages/memory/dist/manager.js +108 -8
- package/packages/memory/dist/projects.d.ts +13 -0
- package/packages/memory/dist/projects.js +211 -0
- package/packages/runtime/dist/index.d.ts +5 -0
- package/packages/runtime-acpx/dist/index.js +6 -0
- package/packages/runtime-cli/dist/claude-turn.d.ts +1 -0
- package/packages/runtime-cli/dist/claude-turn.js +1 -0
- package/packages/runtime-cli/dist/codex-app-server.d.ts +1 -0
- package/packages/runtime-cli/dist/codex-app-server.js +3 -3
- package/packages/runtime-cli/dist/codex-json-rpc.js +32 -8
- package/packages/runtime-cli/dist/codex-protocol.d.ts +1 -1
- package/packages/runtime-cli/dist/codex-protocol.js +4 -1
- package/packages/runtime-cli/dist/index.js +2 -0
- package/packages/shared/dist/crypto.d.ts +28 -1
- package/packages/shared/dist/crypto.js +89 -1
- package/packages/shared/dist/protocol-schemas.d.ts +2 -0
- package/packages/shared/dist/protocol-schemas.js +1 -0
- package/packages/shared/dist/protocol-types.d.ts +74 -1
- package/packages/shared/dist/protocol.d.ts +38 -2
- package/packages/shared/dist/protocol.js +41 -1
- package/apps/cli/dist/commands/interaction.d.ts +0 -2
- package/apps/cli/dist/commands/interaction.js +0 -416
- package/apps/cli/dist/commands/lib/loop-store.d.ts +0 -9
- package/apps/cli/dist/commands/lib/loop-store.js +0 -88
- package/apps/daemon/dist/agent-interaction-ipc.d.ts +0 -45
- package/apps/daemon/dist/agent-interaction-ipc.js +0 -578
- package/apps/daemon/dist/agent-interaction-mcp.d.ts +0 -6
- package/apps/daemon/dist/agent-interaction-mcp.js +0 -259
- package/apps/daemon/dist/interaction-claim-history.d.ts +0 -4
- package/apps/daemon/dist/interaction-claim-history.js +0 -51
- package/apps/daemon/dist/interaction-claim-journal.d.ts +0 -20
- package/apps/daemon/dist/interaction-claim-journal.js +0 -139
- package/apps/daemon/dist/interaction-local-state.d.ts +0 -3
- package/apps/daemon/dist/interaction-local-state.js +0 -12
- package/apps/daemon/dist/interaction-privacy.d.ts +0 -10
- package/apps/daemon/dist/interaction-privacy.js +0 -23
- package/apps/daemon/dist/interaction-prompt-redaction-cache.d.ts +0 -7
- package/apps/daemon/dist/interaction-prompt-redaction-cache.js +0 -254
- package/apps/daemon/dist/interaction-runtime.d.ts +0 -58
- package/apps/daemon/dist/interaction-runtime.js +0 -674
- package/apps/daemon/dist/lifecycle/index.d.ts +0 -7
- package/apps/daemon/dist/lifecycle/index.js +0 -6
- package/apps/relay/dist/session-record-handlers.d.ts +0 -15
- package/apps/relay/dist/session-record-handlers.js +0 -148
- package/apps/relay/dist/session-record-repository.d.ts +0 -38
- package/apps/relay/dist/session-record-repository.js +0 -312
- package/packages/runtime-cli/dist/session-environment.d.ts +0 -7
- package/packages/runtime-cli/dist/session-environment.js +0 -24
- package/packages/shared/dist/interaction.d.ts +0 -95
- package/packages/shared/dist/interaction.js +0 -169
- package/packages/shared/dist/session-record.d.ts +0 -2
- package/packages/shared/dist/session-record.js +0 -4
|
@@ -923,6 +923,7 @@ async function runGatewayPrompt(ws, config, gateway, binding, input, session, le
|
|
|
923
923
|
}
|
|
924
924
|
const started = await startGatewayProviderTurnWithFence(lease, input, binding, () => withCancelSettlement(getRuntime(session.cwd).startTurn({
|
|
925
925
|
handle: session.handle,
|
|
926
|
+
applicationContext: prepared.applicationContext,
|
|
926
927
|
text: prepared.text,
|
|
927
928
|
mode: "prompt",
|
|
928
929
|
permissionMode,
|
|
@@ -17831,6 +17831,7 @@ var capabilitiesSchema = external_exports.object({
|
|
|
17831
17831
|
memory: external_exports.boolean().optional(),
|
|
17832
17832
|
memoryTurnOptionsV1: external_exports.boolean().optional(),
|
|
17833
17833
|
memoryCommandsV1: external_exports.boolean().optional(),
|
|
17834
|
+
memoryProjectsV1: external_exports.boolean().optional(),
|
|
17834
17835
|
gatewayCommandsV1: external_exports.boolean().optional(),
|
|
17835
17836
|
filePreview: external_exports.boolean().optional(),
|
|
17836
17837
|
scheduleAuthContextV1: external_exports.boolean().optional(),
|
|
@@ -18169,6 +18170,14 @@ var controllerMessageSchema = external_exports.discriminatedUnion("type", [
|
|
|
18169
18170
|
encryptedPayload: encryptedEnvelopeSchema,
|
|
18170
18171
|
semanticDigest: external_exports.string().min(1)
|
|
18171
18172
|
}),
|
|
18173
|
+
external_exports.object({
|
|
18174
|
+
type: external_exports.literal("controller:memoryProjectCommand"),
|
|
18175
|
+
requestId: external_exports.string().min(1),
|
|
18176
|
+
machineId: external_exports.string().min(1),
|
|
18177
|
+
action: external_exports.enum(["list", "overview", "read", "search", "reindex"]),
|
|
18178
|
+
encryptedPayload: encryptedEnvelopeSchema,
|
|
18179
|
+
semanticDigest: external_exports.string().min(1)
|
|
18180
|
+
}),
|
|
18172
18181
|
external_exports.object({
|
|
18173
18182
|
type: external_exports.literal("controller:gatewayCommand"),
|
|
18174
18183
|
requestId: external_exports.string().min(1),
|
|
@@ -18532,6 +18541,13 @@ var machineMessageSchema = external_exports.discriminatedUnion("type", [
|
|
|
18532
18541
|
encryptedPayload: encryptedEnvelopeSchema,
|
|
18533
18542
|
messageId: external_exports.string().min(1).optional()
|
|
18534
18543
|
}),
|
|
18544
|
+
external_exports.object({
|
|
18545
|
+
type: external_exports.literal("machine:memoryProjectResult"),
|
|
18546
|
+
requestId: external_exports.string().min(1),
|
|
18547
|
+
action: external_exports.enum(["list", "overview", "read", "search", "reindex"]),
|
|
18548
|
+
encryptedPayload: encryptedEnvelopeSchema,
|
|
18549
|
+
messageId: external_exports.string().min(1).optional()
|
|
18550
|
+
}),
|
|
18535
18551
|
external_exports.object({
|
|
18536
18552
|
type: external_exports.literal("machine:gatewayResult"),
|
|
18537
18553
|
requestId: external_exports.string().min(1),
|
|
@@ -18568,7 +18584,8 @@ var clientMessageSchema = external_exports.union([
|
|
|
18568
18584
|
capabilities: external_exports.object({
|
|
18569
18585
|
daemonLifecycleEventsV1: external_exports.boolean().optional(),
|
|
18570
18586
|
daemonAutostartV1: external_exports.boolean().optional(),
|
|
18571
|
-
resourcePreferenceInvalidationV1: external_exports.boolean().optional()
|
|
18587
|
+
resourcePreferenceInvalidationV1: external_exports.boolean().optional(),
|
|
18588
|
+
daemonProjectsPreferenceV1: external_exports.boolean().optional()
|
|
18572
18589
|
}).optional()
|
|
18573
18590
|
}),
|
|
18574
18591
|
controllerMessageSchema,
|
|
@@ -18610,6 +18627,21 @@ var machineCommandSchema = external_exports.discriminatedUnion("type", [
|
|
|
18610
18627
|
semanticDigest: external_exports.string().min(1),
|
|
18611
18628
|
authorization: scheduleAuthorizationContextV1Schema
|
|
18612
18629
|
}),
|
|
18630
|
+
external_exports.object({
|
|
18631
|
+
type: external_exports.literal("machine:memoryProjectCommand"),
|
|
18632
|
+
requestId: external_exports.string().min(1),
|
|
18633
|
+
controllerRequestId: external_exports.string().min(1),
|
|
18634
|
+
machineId: external_exports.string().min(1),
|
|
18635
|
+
action: external_exports.enum(["list", "overview", "read", "search", "reindex"]),
|
|
18636
|
+
encryptedPayload: encryptedEnvelopeSchema,
|
|
18637
|
+
semanticDigest: external_exports.string().min(1),
|
|
18638
|
+
projectCandidates: external_exports.array(external_exports.object({
|
|
18639
|
+
sessionId: external_exports.string().min(1),
|
|
18640
|
+
cwd: external_exports.string().min(1).max(4096).refine((value) => !value.includes("\0"), "cwd must not contain NUL"),
|
|
18641
|
+
updatedAt: external_exports.number().int().nonnegative()
|
|
18642
|
+
})).max(500),
|
|
18643
|
+
authorization: scheduleAuthorizationContextV1Schema
|
|
18644
|
+
}),
|
|
18613
18645
|
external_exports.object({
|
|
18614
18646
|
type: external_exports.literal("machine:gatewayCommand"),
|
|
18615
18647
|
requestId: external_exports.string().min(1),
|
|
@@ -18771,6 +18803,7 @@ var _machineClientMessageTypeSchema = external_exports.enum([
|
|
|
18771
18803
|
"machine:cancelResult",
|
|
18772
18804
|
"machine:scheduleResult",
|
|
18773
18805
|
"machine:memoryResult",
|
|
18806
|
+
"machine:memoryProjectResult",
|
|
18774
18807
|
"machine:gatewayResult",
|
|
18775
18808
|
"machine:daemonLifecycleResult",
|
|
18776
18809
|
"machine:error"
|
|
@@ -18783,6 +18816,7 @@ var serverMessageSchema = external_exports.union([
|
|
|
18783
18816
|
machineMessages: external_exports.array(external_exports.string().min(1)).optional(),
|
|
18784
18817
|
scheduleCreateSemanticDigestV1: external_exports.boolean().optional(),
|
|
18785
18818
|
memoryCommandsV1: external_exports.boolean().optional(),
|
|
18819
|
+
memoryProjectsV1: external_exports.boolean().optional(),
|
|
18786
18820
|
memoryTurnOptionsV1: external_exports.boolean().optional(),
|
|
18787
18821
|
gatewayCommandsV1: external_exports.boolean().optional(),
|
|
18788
18822
|
enrollmentInvitesV1: external_exports.boolean().optional(),
|
|
@@ -18869,6 +18903,13 @@ var serverMessageSchema = external_exports.union([
|
|
|
18869
18903
|
action: external_exports.string().min(1),
|
|
18870
18904
|
encryptedPayload: encryptedEnvelopeSchema
|
|
18871
18905
|
}),
|
|
18906
|
+
external_exports.object({
|
|
18907
|
+
type: external_exports.literal("server:memoryProjectResult"),
|
|
18908
|
+
requestId: external_exports.string().min(1),
|
|
18909
|
+
machineId: external_exports.string().min(1),
|
|
18910
|
+
action: external_exports.enum(["list", "overview", "read", "search", "reindex"]),
|
|
18911
|
+
encryptedPayload: encryptedEnvelopeSchema
|
|
18912
|
+
}),
|
|
18872
18913
|
external_exports.object({
|
|
18873
18914
|
type: external_exports.literal("server:gatewayResult"),
|
|
18874
18915
|
requestId: external_exports.string().min(1),
|
|
@@ -18888,7 +18929,7 @@ var serverMessageSchema = external_exports.union([
|
|
|
18888
18929
|
type: external_exports.literal("server:resourcePreferenceInvalidated"),
|
|
18889
18930
|
resourceType: external_exports.enum(["session", "daemon"]),
|
|
18890
18931
|
resourceId: external_exports.string().min(1),
|
|
18891
|
-
preferenceKey: external_exports.enum(["session.star.v1", "daemon.alias.v1"]),
|
|
18932
|
+
preferenceKey: external_exports.enum(["session.star.v1", "daemon.alias.v1", "daemon.projects.v1"]),
|
|
18892
18933
|
revision: external_exports.number().int().positive()
|
|
18893
18934
|
}),
|
|
18894
18935
|
external_exports.object({
|
|
@@ -20539,10 +20580,9 @@ function windowsTaskBackend(run) {
|
|
|
20539
20580
|
return {
|
|
20540
20581
|
async inspect(spec) {
|
|
20541
20582
|
const [result, identity] = await Promise.all([
|
|
20542
|
-
run
|
|
20583
|
+
inspectWindowsTask(run, command, spec.serviceId),
|
|
20543
20584
|
run(identityCommand, ["/user", "/fo", "csv", "/nh"])
|
|
20544
20585
|
]);
|
|
20545
|
-
requireManagerInspectionQuery(result, "schtasks /Query");
|
|
20546
20586
|
const registered = result.exitCode === 0;
|
|
20547
20587
|
if (registered)
|
|
20548
20588
|
requireManagerInspectionSuccess(identity, "whoami /user");
|
|
@@ -20576,6 +20616,15 @@ function windowsTaskBackend(run) {
|
|
|
20576
20616
|
}
|
|
20577
20617
|
};
|
|
20578
20618
|
}
|
|
20619
|
+
async function inspectWindowsTask(run, command, serviceId) {
|
|
20620
|
+
const result = await run(command, ["/Query", "/TN", serviceId, "/XML"]);
|
|
20621
|
+
requireManagerInspectionSettled(result, "schtasks /Query");
|
|
20622
|
+
if (result.exitCode === 0 || managerObjectMissing(result))
|
|
20623
|
+
return result;
|
|
20624
|
+
const managerProbe = await run(command, ["/Query", "/FO", "CSV", "/NH"]);
|
|
20625
|
+
requireManagerInspectionSuccess(managerProbe, "schtasks /Query");
|
|
20626
|
+
return result;
|
|
20627
|
+
}
|
|
20579
20628
|
function windowsTaskDefinitionMatches(spec, xmlSource, userSid) {
|
|
20580
20629
|
const triggers = singleXmlElementBody(xmlSource, "Triggers");
|
|
20581
20630
|
const principals = singleXmlElementBody(xmlSource, "Principals");
|
|
@@ -242,6 +242,7 @@ async function runLoopOnce(ws, config, loop, reason) {
|
|
|
242
242
|
};
|
|
243
243
|
const turn = withCancelSettlement(runtime.startTurn({
|
|
244
244
|
handle: turnHandle,
|
|
245
|
+
applicationContext: prepared.applicationContext,
|
|
245
246
|
text: prepared.text,
|
|
246
247
|
mode: "prompt",
|
|
247
248
|
permissionMode,
|
|
@@ -3,7 +3,7 @@ import { canonicalizeMemoryCommandSemanticsV1, decryptJson, encryptJson, memoryC
|
|
|
3
3
|
import { listMemoryFiles, MemoryError, scopeKeyFor } from "../../../../packages/memory/dist/index.js";
|
|
4
4
|
import { appendAudit } from "../audit.js";
|
|
5
5
|
import { claimCommandRequest, sendCommandResponse } from "../relay/send.js";
|
|
6
|
-
import {
|
|
6
|
+
import { withMemoryManager } from "./managers.js";
|
|
7
7
|
const MAX_READ_BYTES = 256 * 1024;
|
|
8
8
|
export async function handleMemoryCommand(ws, config, command) {
|
|
9
9
|
if (command.type !== "machine:memoryCommand")
|
|
@@ -36,8 +36,48 @@ export async function handleMemoryCommand(ws, config, command) {
|
|
|
36
36
|
if (semantics.action !== command.action || !digestEqual(memoryCommandSemanticDigestV1(config.accountSecret, semantics), command.semanticDigest)) {
|
|
37
37
|
throw memoryError("Memory command semantic digest is invalid", "MEMORY_COMMAND_INVALID");
|
|
38
38
|
}
|
|
39
|
-
const
|
|
40
|
-
|
|
39
|
+
const handled = await withMemoryManager(config, trustedSession.cwd, async (manager) => {
|
|
40
|
+
switch (command.action) {
|
|
41
|
+
case "overview": {
|
|
42
|
+
const [status, files] = await Promise.all([manager.status(), listMemoryFiles(manager.rootDir)]);
|
|
43
|
+
await respond(ws, config, command, { enabled: true, status: publicStatus(status), files });
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
case "read": {
|
|
47
|
+
if (!semantics.path)
|
|
48
|
+
throw memoryError("Memory path is missing", "MEMORY_COMMAND_INVALID");
|
|
49
|
+
const from = semantics.from ?? 1;
|
|
50
|
+
const requestedLines = semantics.lines ?? 500;
|
|
51
|
+
const read = await manager.readMemory(semantics.path, { from, lines: requestedLines });
|
|
52
|
+
const limited = truncateUtf8(read.text, MAX_READ_BYTES);
|
|
53
|
+
const lines = limited.text.length === 0 ? 0 : limited.text.split("\n").length;
|
|
54
|
+
await respond(ws, config, command, {
|
|
55
|
+
path: read.path,
|
|
56
|
+
text: limited.text,
|
|
57
|
+
from,
|
|
58
|
+
lines,
|
|
59
|
+
truncated: limited.truncated || read.endLine < read.totalLines,
|
|
60
|
+
});
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
case "search": {
|
|
64
|
+
if (!semantics.query)
|
|
65
|
+
throw memoryError("Memory query is missing", "MEMORY_COMMAND_INVALID");
|
|
66
|
+
const results = await manager.search(semantics.query, {
|
|
67
|
+
maxResults: semantics.maxResults,
|
|
68
|
+
...(semantics.minScore === undefined ? {} : { minScore: semantics.minScore }),
|
|
69
|
+
});
|
|
70
|
+
await respond(ws, config, command, { results });
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
case "reindex": {
|
|
74
|
+
const sync = await manager.sync({ force: true });
|
|
75
|
+
await respond(ws, config, command, { sync, status: publicStatus(await manager.status()) });
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
if (handled === null) {
|
|
41
81
|
if (command.action === "overview") {
|
|
42
82
|
await respond(ws, config, command, {
|
|
43
83
|
enabled: false,
|
|
@@ -55,45 +95,7 @@ export async function handleMemoryCommand(ws, config, command) {
|
|
|
55
95
|
}
|
|
56
96
|
throw memoryError("Memory is disabled on this machine", "MEMORY_DISABLED");
|
|
57
97
|
}
|
|
58
|
-
|
|
59
|
-
case "overview": {
|
|
60
|
-
const [status, files] = await Promise.all([manager.status(), listMemoryFiles(manager.rootDir)]);
|
|
61
|
-
await respond(ws, config, command, { enabled: true, status: publicStatus(status), files });
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
case "read": {
|
|
65
|
-
if (!semantics.path)
|
|
66
|
-
throw memoryError("Memory path is missing", "MEMORY_COMMAND_INVALID");
|
|
67
|
-
const from = semantics.from ?? 1;
|
|
68
|
-
const requestedLines = semantics.lines ?? 500;
|
|
69
|
-
const read = await manager.readMemory(semantics.path, { from, lines: requestedLines });
|
|
70
|
-
const limited = truncateUtf8(read.text, MAX_READ_BYTES);
|
|
71
|
-
const lines = limited.text.length === 0 ? 0 : limited.text.split("\n").length;
|
|
72
|
-
await respond(ws, config, command, {
|
|
73
|
-
path: read.path,
|
|
74
|
-
text: limited.text,
|
|
75
|
-
from,
|
|
76
|
-
lines,
|
|
77
|
-
truncated: limited.truncated || read.endLine < read.totalLines,
|
|
78
|
-
});
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
case "search": {
|
|
82
|
-
if (!semantics.query)
|
|
83
|
-
throw memoryError("Memory query is missing", "MEMORY_COMMAND_INVALID");
|
|
84
|
-
const results = await manager.search(semantics.query, {
|
|
85
|
-
maxResults: semantics.maxResults,
|
|
86
|
-
...(semantics.minScore === undefined ? {} : { minScore: semantics.minScore }),
|
|
87
|
-
});
|
|
88
|
-
await respond(ws, config, command, { results });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
case "reindex": {
|
|
92
|
-
const sync = await manager.sync({ force: true });
|
|
93
|
-
await respond(ws, config, command, { sync, status: publicStatus(await manager.status()) });
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
98
|
+
return;
|
|
97
99
|
}
|
|
98
100
|
catch (error) {
|
|
99
101
|
await appendAudit({
|
|
@@ -61,6 +61,7 @@ export async function maybeRunMemoryFlush(ws, config, session, metadata) {
|
|
|
61
61
|
memoryAccessLease = prepared.lease;
|
|
62
62
|
const turn = getRuntime(session.cwd).startTurn({
|
|
63
63
|
handle: session.handle,
|
|
64
|
+
applicationContext: prepared.applicationContext,
|
|
64
65
|
text: prepared.text,
|
|
65
66
|
mode: "prompt",
|
|
66
67
|
requestId,
|
|
@@ -2,7 +2,7 @@ import { createMemoryAccessLease, revokeCurrentProcessMemoryAccess, } from "../.
|
|
|
2
2
|
import { BoundedTtlCache } from "../turn-coordinator.js";
|
|
3
3
|
import { memoryEnabled, resolveMemoryMode } from "./config.js";
|
|
4
4
|
import { MEMORY_CLI_LAUNCHER, memoryInstructions } from "./instructions.js";
|
|
5
|
-
import {
|
|
5
|
+
import { withMemoryManager } from "./managers.js";
|
|
6
6
|
import { assertMemoryAccessHealthy } from "./access-lifecycle.js";
|
|
7
7
|
const injectedSessionLimit = 1000;
|
|
8
8
|
const injectedSessionTtlMs = 24 * 60 * 60 * 1000;
|
|
@@ -26,14 +26,15 @@ export function prepareMemoryPrompt(input) {
|
|
|
26
26
|
return { text: input.text, memoryMode, lease: granted.lease };
|
|
27
27
|
// Warm the workspace index/watcher so agent writes via the memory CLI are
|
|
28
28
|
// re-indexed promptly. Fire-and-forget; prompt latency is unaffected.
|
|
29
|
-
void
|
|
29
|
+
void withMemoryManager(input.config, input.cwd, async () => undefined).catch(() => undefined);
|
|
30
30
|
const injectionKey = input.injectionKey ?? input.sessionId;
|
|
31
31
|
const capabilityPrefix = granted.accessToken
|
|
32
32
|
? `Memory write capability for this turn: add \`--access-token ${granted.accessToken}\` to every \`${MEMORY_CLI_LAUNCHER} memory write\` command.`
|
|
33
33
|
: "";
|
|
34
34
|
if (injectedSessions.get(injectionKey) === memoryMode) {
|
|
35
35
|
return {
|
|
36
|
-
text:
|
|
36
|
+
text: input.text,
|
|
37
|
+
...(capabilityPrefix ? { applicationContext: capabilityPrefix } : {}),
|
|
37
38
|
memoryMode,
|
|
38
39
|
...(granted.accessToken ? { accessToken: granted.accessToken } : {}),
|
|
39
40
|
lease: granted.lease,
|
|
@@ -44,7 +45,8 @@ export function prepareMemoryPrompt(input) {
|
|
|
44
45
|
return { text: input.text, memoryMode, lease: granted.lease };
|
|
45
46
|
injectedSessions.set(injectionKey, memoryMode);
|
|
46
47
|
return {
|
|
47
|
-
text:
|
|
48
|
+
text: input.text,
|
|
49
|
+
applicationContext: `${instructions}${capabilityPrefix ? `\n\n${capabilityPrefix}` : ""}`,
|
|
48
50
|
memoryMode,
|
|
49
51
|
...(granted.accessToken ? { accessToken: granted.accessToken } : {}),
|
|
50
52
|
lease: granted.lease,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { MemoryManager } from "../../../../packages/memory/dist/index.js";
|
|
2
2
|
import type { DaemonConfig } from "../types.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Borrows one workspace manager for the complete duration of an operation.
|
|
5
|
+
* Cache eviction may close only idle managers; a bulk all-project search can
|
|
6
|
+
* therefore cross the cache limit without closing a database underneath an
|
|
7
|
+
* in-flight search or sync.
|
|
6
8
|
*/
|
|
7
|
-
export declare function
|
|
9
|
+
export declare function withMemoryManager<T>(config: DaemonConfig, cwd: string, action: (manager: MemoryManager) => Promise<T>): Promise<T | null>;
|
|
8
10
|
export declare function closeMemoryManagers(): Promise<void>;
|
|
@@ -1,53 +1,56 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { MemoryManager, revokeCurrentProcessMemoryAccess } from "../../../../packages/memory/dist/index.js";
|
|
2
|
+
import { listRegisteredMemoryProjects, MemoryManager, registerMemoryProject, revokeCurrentProcessMemoryAccess, } from "../../../../packages/memory/dist/index.js";
|
|
3
3
|
import { appendAudit } from "../audit.js";
|
|
4
4
|
import { memoryEnabled } from "./config.js";
|
|
5
|
-
const managers = new Map();
|
|
6
5
|
const managerLimit = 32;
|
|
6
|
+
const managers = new Map();
|
|
7
|
+
const closingManagers = new Map();
|
|
7
8
|
async function createManager(config, cwd) {
|
|
8
9
|
const manager = new MemoryManager({
|
|
9
10
|
scope: { kind: "workspace", cwd },
|
|
10
11
|
embedding: config.memory?.embedding === "none" ? "none" : "auto",
|
|
11
12
|
openai: config.memory?.openai,
|
|
12
13
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
try {
|
|
15
|
+
await manager.init();
|
|
16
|
+
await manager.sync();
|
|
17
|
+
await registerMemoryProject(cwd);
|
|
18
|
+
manager.startWatcher();
|
|
19
|
+
return manager;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
await manager.close().catch(() => undefined);
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
17
25
|
}
|
|
18
26
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
27
|
+
* Borrows one workspace manager for the complete duration of an operation.
|
|
28
|
+
* Cache eviction may close only idle managers; a bulk all-project search can
|
|
29
|
+
* therefore cross the cache limit without closing a database underneath an
|
|
30
|
+
* in-flight search or sync.
|
|
21
31
|
*/
|
|
22
|
-
export async function
|
|
32
|
+
export async function withMemoryManager(config, cwd, action) {
|
|
23
33
|
if (!memoryEnabled(config))
|
|
24
34
|
return null;
|
|
25
|
-
//
|
|
35
|
+
// Validate on every access, including cache hits. A future or malformed
|
|
36
|
+
// project registry must fail closed before an existing manager can continue
|
|
37
|
+
// reading or mutating adjacent Memory state.
|
|
38
|
+
await listRegisteredMemoryProjects();
|
|
26
39
|
const key = `workspace:${path.resolve(cwd)}`;
|
|
27
|
-
let
|
|
28
|
-
if (!
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
machineId: config.machineId,
|
|
41
|
-
actor: "system",
|
|
42
|
-
action: "memory.manager.failed",
|
|
43
|
-
summary: `Failed to initialize memory manager for ${cwd}`,
|
|
44
|
-
evidence: { error: error instanceof Error ? error.message : String(error) },
|
|
45
|
-
});
|
|
46
|
-
throw error;
|
|
47
|
-
});
|
|
48
|
-
managers.set(key, pending);
|
|
40
|
+
let entry = managers.get(key);
|
|
41
|
+
if (!entry) {
|
|
42
|
+
entry = createManagerEntry(config, cwd, key);
|
|
43
|
+
managers.set(key, entry);
|
|
44
|
+
}
|
|
45
|
+
entry.borrowers += 1;
|
|
46
|
+
trimIdleManagers();
|
|
47
|
+
try {
|
|
48
|
+
return await action(await entry.pending);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
entry.borrowers -= 1;
|
|
52
|
+
trimIdleManagers();
|
|
49
53
|
}
|
|
50
|
-
return pending;
|
|
51
54
|
}
|
|
52
55
|
export async function closeMemoryManagers() {
|
|
53
56
|
let revokeError;
|
|
@@ -57,12 +60,85 @@ export async function closeMemoryManagers() {
|
|
|
57
60
|
catch (error) {
|
|
58
61
|
revokeError = error;
|
|
59
62
|
}
|
|
60
|
-
const pending = [...managers.values()];
|
|
63
|
+
const pending = [...managers.values()].map((entry) => entry.pending);
|
|
61
64
|
managers.clear();
|
|
62
65
|
for (const managerPromise of pending) {
|
|
63
66
|
const manager = await managerPromise.catch(() => null);
|
|
64
67
|
await manager?.close().catch(() => undefined);
|
|
65
68
|
}
|
|
69
|
+
await Promise.allSettled([...closingManagers.values()].map((entry) => entry.pending));
|
|
70
|
+
closingManagers.clear();
|
|
66
71
|
if (revokeError)
|
|
67
72
|
throw revokeError;
|
|
68
73
|
}
|
|
74
|
+
function createManagerEntry(config, cwd, key) {
|
|
75
|
+
const entry = {
|
|
76
|
+
borrowers: 0,
|
|
77
|
+
pending: Promise.resolve(null),
|
|
78
|
+
};
|
|
79
|
+
entry.pending = (async () => {
|
|
80
|
+
// An evicted manager may still be completing a watcher sync. Do not open a
|
|
81
|
+
// replacement database for the same workspace until that exact close has
|
|
82
|
+
// settled.
|
|
83
|
+
const closing = closingManagers.get(key);
|
|
84
|
+
if (closing) {
|
|
85
|
+
await closing.pending;
|
|
86
|
+
if (closing.error) {
|
|
87
|
+
throw memoryManagerCloseError();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return await createManager(config, cwd);
|
|
91
|
+
})().catch(async (error) => {
|
|
92
|
+
if (managers.get(key) === entry)
|
|
93
|
+
managers.delete(key);
|
|
94
|
+
await appendAudit({
|
|
95
|
+
machineId: config.machineId,
|
|
96
|
+
actor: "system",
|
|
97
|
+
action: "memory.manager.failed",
|
|
98
|
+
summary: `Failed to initialize memory manager for ${cwd}`,
|
|
99
|
+
evidence: { error: error instanceof Error ? error.message : String(error) },
|
|
100
|
+
});
|
|
101
|
+
throw error;
|
|
102
|
+
});
|
|
103
|
+
return entry;
|
|
104
|
+
}
|
|
105
|
+
function trimIdleManagers() {
|
|
106
|
+
while (managers.size > managerLimit) {
|
|
107
|
+
const idle = [...managers.entries()].find(([, entry]) => entry.borrowers === 0);
|
|
108
|
+
if (!idle)
|
|
109
|
+
return;
|
|
110
|
+
const [key, entry] = idle;
|
|
111
|
+
managers.delete(key);
|
|
112
|
+
closeManagerEntry(key, entry);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function closeManagerEntry(key, entry) {
|
|
116
|
+
if (closingManagers.has(key))
|
|
117
|
+
return;
|
|
118
|
+
const closing = {
|
|
119
|
+
error: null,
|
|
120
|
+
pending: Promise.resolve(),
|
|
121
|
+
};
|
|
122
|
+
closing.pending = entry.pending
|
|
123
|
+
.then(async (manager) => {
|
|
124
|
+
try {
|
|
125
|
+
await manager.close();
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
closing.error = error;
|
|
129
|
+
}
|
|
130
|
+
}, () => undefined)
|
|
131
|
+
.finally(() => {
|
|
132
|
+
// A failed close is an ownership fence, not a best-effort cleanup. Keep
|
|
133
|
+
// it until daemon shutdown so another manager cannot open the same DB
|
|
134
|
+
// while the old watcher/SQLite handle may still be alive.
|
|
135
|
+
if (closing.error === null && closingManagers.get(key) === closing)
|
|
136
|
+
closingManagers.delete(key);
|
|
137
|
+
});
|
|
138
|
+
closingManagers.set(key, closing);
|
|
139
|
+
}
|
|
140
|
+
function memoryManagerCloseError() {
|
|
141
|
+
const error = new Error("Previous Memory manager did not close safely");
|
|
142
|
+
error.code = "MEMORY_MANAGER_CLOSE_FAILED";
|
|
143
|
+
return error;
|
|
144
|
+
}
|