@cursor/july 0.2.0 → 0.2.1
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/dist/channels/slack/channel-watch.d.ts +19 -3
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +48 -9
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -4
- package/dist/channels/slack/types.d.ts +11 -12
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/assets/{app.9eAtsjAM.js → app.D23Y-7Tp.js} +4 -4
- package/dist/docs/assets/chunks/@localSearchIndexroot.CzCCM7N8.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.C9s69nxj.js → VPLocalSearchBox.CWBeTFRZ.js} +1 -1
- package/dist/docs/assets/chunks/{arc.DmWDRaF-.js → arc.DSF2O3pm.js} +1 -1
- package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.BE1Wj4f3.js → architectureDiagram-Q4EWVU46.J52Wzbkg.js} +1 -1
- package/dist/docs/assets/chunks/{baseUniq.pmEZGnWu.js → baseUniq.CQS3LPCt.js} +1 -1
- package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.D6mM-5XN.js → blockDiagram-DXYQGD6D.Dw339Gr5.js} +1 -1
- package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.ZQYTPC1b.js → c4Diagram-AHTNJAMY.BUdtOaRZ.js} +1 -1
- package/dist/docs/assets/chunks/channel.Bfu4df88.js +1 -0
- package/dist/docs/assets/chunks/{chunk-4BX2VUAB.CKJ7gaK8.js → chunk-4BX2VUAB.CuOrkEqk.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-4TB4RGXK.CL61HnvB.js → chunk-4TB4RGXK.BJNBcY7U.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-55IACEB6.DsA4XA1m.js → chunk-55IACEB6.VJK5LAm_.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-EDXVE4YY.BODHCHFW.js → chunk-EDXVE4YY.BYYLihvj.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-FMBD7UC4.D6NBaZTa.js → chunk-FMBD7UC4.CmoW8BXP.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-OYMX7WX6.BLPuqFXg.js → chunk-OYMX7WX6.DTGY4C-M.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-QZHKN3VN.BFs_ML8Z.js → chunk-QZHKN3VN.Cg5n67vl.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-YZCP3GAM.BDjuFyZv.js → chunk-YZCP3GAM.C3GR_ia5.js} +1 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.DdfgtaWs.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.DdfgtaWs.js +1 -0
- package/dist/docs/assets/chunks/clone.rkmfti6d.js +1 -0
- package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.D85HNEsi.js → cose-bilkent-S5V4N54A.BTRG8N3b.js} +1 -1
- package/dist/docs/assets/chunks/{dagre-KV5264BT.V463KlSF.js → dagre-KV5264BT.Bob_bp_p.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-5BDNPKRD.DhBDu1ab.js → diagram-5BDNPKRD.ggPcs9uO.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.v6sC68zl.js → diagram-G4DWMVQ6.BP0qyJkp.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-MMDJMWI5.DhHsdXYv.js → diagram-MMDJMWI5.B0X24UKr.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-TYMM5635.BPWNSFZc.js → diagram-TYMM5635.B4rXHFVt.js} +1 -1
- package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.Cq0wTMPL.js → erDiagram-SMLLAGMA._55Rt9oX.js} +1 -1
- package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.MveMtucC.js → flowDiagram-DWJPFMVM.DGP4XvR5.js} +1 -1
- package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.BzKfGUSf.js → ganttDiagram-T4ZO3ILL.BtXtkL4E.js} +1 -1
- package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.OToXTSW_.js → gitGraphDiagram-UUTBAWPF.B9cPWblK.js} +1 -1
- package/dist/docs/assets/chunks/{graph.D82tam-l.js → graph.D8HzNexS.js} +1 -1
- package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.DzFlRmcE.js → infoDiagram-42DDH7IO.Bw7CQUpi.js} +1 -1
- package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.CVPRJiKe.js → ishikawaDiagram-UXIWVN3A.MwkzF6nQ.js} +1 -1
- package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.CQvNTfQC.js → journeyDiagram-VCZTEJTY.DIGFF-3C.js} +1 -1
- package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.BwywywUl.js → kanban-definition-6JOO6SKY.DhYef2BN.js} +1 -1
- package/dist/docs/assets/chunks/{layout.C4BkPPba.js → layout.C0XUxuPi.js} +1 -1
- package/dist/docs/assets/chunks/{linear.FoSfGKD4.js → linear.BwNPpZex.js} +1 -1
- package/dist/docs/assets/chunks/{min.yLh8jqfl.js → min.CwAQdL7z.js} +1 -1
- package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.C9Dq2_CN.js → mindmap-definition-QFDTVHPH.pWsSVLsP.js} +1 -1
- package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.CiGyBcES.js → pieDiagram-DEJITSTG.BDJ3FbBy.js} +1 -1
- package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.1FWea9nj.js → quadrantDiagram-34T5L4WZ.Co80izyB.js} +1 -1
- package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.B1q7ntiV.js → requirementDiagram-MS252O5E.JveKw4yx.js} +1 -1
- package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.Cpr4oU_k.js → sankeyDiagram-XADWPNL6.B0A7adPi.js} +1 -1
- package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.CUK68STn.js → sequenceDiagram-FGHM5R23.d6JZ5Hre.js} +1 -1
- package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.zU0yL2m4.js → stateDiagram-FHFEXIEX.DWnL0NQl.js} +1 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.ZEetPk0E.js +1 -0
- package/dist/docs/assets/chunks/{theme.DhnKd0CD.js → theme.MJTLx0hh.js} +2 -2
- package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.CNYoXo3F.js → timeline-definition-GMOUNBTQ.CFS7Ai4c.js} +1 -1
- package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.DFWH3G9s.js → vennDiagram-DHZGUBPP.CwSlnjCf.js} +1 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.3gurI8YA.js +162 -0
- package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.7WLkqM7s.js → wardleyDiagram-NUSXRM2D.B_8mvtjh.js} +1 -1
- package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.-SyUNgWw.js → xychartDiagram-5P7HB3ND.DtjU5H85.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.DVjNyqq5.js → guides_slack.md.Bjw2r2gL.js} +4 -4
- package/dist/docs/assets/{guides_slack.md.DVjNyqq5.lean.js → guides_slack.md.Bjw2r2gL.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +35 -35
- package/dist/docs/deployment.html +35 -35
- package/dist/docs/evals.html +35 -35
- package/dist/docs/guides/agent-to-agent.html +35 -35
- package/dist/docs/guides/bitbucket.html +35 -35
- package/dist/docs/guides/cloud-agents.html +35 -35
- package/dist/docs/guides/convert-automation.html +35 -35
- package/dist/docs/guides/github.html +35 -35
- package/dist/docs/guides/gitlab.html +35 -35
- package/dist/docs/guides/grokbot-agents.html +35 -35
- package/dist/docs/guides/hooks.html +35 -35
- package/dist/docs/guides/improve.html +35 -35
- package/dist/docs/guides/jev.html +35 -35
- package/dist/docs/guides/mcp-oauth.html +35 -35
- package/dist/docs/guides/opentelemetry.html +35 -35
- package/dist/docs/guides/slack.html +39 -39
- package/dist/docs/guides/slack.md +15 -7
- package/dist/docs/guides/webhooks.html +35 -35
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +35 -35
- package/dist/docs/index.html +35 -35
- package/dist/docs/llms-full.txt +15 -7
- package/dist/docs/quickstart.html +35 -35
- package/dist/docs/reference/agent-config.html +35 -35
- package/dist/docs/reference/artifacts.html +35 -35
- package/dist/docs/reference/channels.html +35 -35
- package/dist/docs/reference/cli.html +35 -35
- package/dist/docs/reference/connections.html +35 -35
- package/dist/docs/reference/evals.html +35 -35
- package/dist/docs/reference/extensions.html +35 -35
- package/dist/docs/reference/hooks.html +35 -35
- package/dist/docs/reference/http-api.html +35 -35
- package/dist/docs/reference/instructions.html +35 -35
- package/dist/docs/reference/playground.html +35 -35
- package/dist/docs/reference/project-layout.html +35 -35
- package/dist/docs/reference/prompt.html +35 -35
- package/dist/docs/reference/schedules.html +35 -35
- package/dist/docs/reference/sessions.html +35 -35
- package/dist/docs/reference/skills.html +35 -35
- package/dist/docs/reference/subagents.html +35 -35
- package/dist/docs/reference/tools.html +35 -35
- package/dist/docs/templates/agentic-owners.html +35 -35
- package/dist/docs/templates/pr-autofixer.html +35 -35
- package/dist/docs/templates/security-reviewer.html +35 -35
- package/dist/docs/templates/thermo-quality-review.html +35 -35
- package/dist/docs/templates/thermo-review.html +35 -35
- package/dist/docs/templates/triage.html +35 -35
- package/dist/docs/troubleshooting.html +35 -35
- package/dist/files-backends/cursor-hosted.d.ts +11 -17
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +13 -41
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/artifacts-store.d.ts +11 -0
- package/dist/internal/artifacts-store.d.ts.map +1 -1
- package/dist/internal/artifacts-store.js +113 -18
- package/dist/internal/cursor/cursor-api-transport.d.ts +37 -0
- package/dist/internal/cursor/cursor-api-transport.d.ts.map +1 -0
- package/dist/internal/cursor/cursor-api-transport.js +44 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts +21 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -1
- package/dist/internal/cursor/hosted-store-secrets.js +26 -0
- package/dist/internal/cursor/store-api-client.d.ts +82 -0
- package/dist/internal/cursor/store-api-client.d.ts.map +1 -0
- package/dist/internal/cursor/store-api-client.js +227 -0
- package/dist/internal/deploy-manifest.d.ts +8 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +7 -1
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +13 -4
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +13 -15
- package/dist/internal/store-api-protocol.d.ts +134 -0
- package/dist/internal/store-api-protocol.d.ts.map +1 -0
- package/dist/internal/store-api-protocol.js +126 -0
- package/dist/memory.d.ts +49 -14
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +193 -74
- package/dist/playground/assets/{index-CX6oTKwz.js → index-Cs0MKsv4.js} +30 -30
- package/dist/playground/index.html +1 -1
- package/dist/reminders.d.ts +1 -1
- package/dist/reminders.d.ts.map +1 -1
- package/dist/types.d.ts +28 -1
- package/dist/types.d.ts.map +1 -1
- package/docs/guides/slack.md +15 -7
- package/package.json +1 -1
- package/skills/setup-slack/SKILL.md +1 -1
- package/src/channels/slack/channel-watch.ts +57 -8
- package/src/channels/slack/slack-channel.ts +4 -3
- package/src/channels/slack/types.ts +11 -12
- package/src/files-backends/cursor-hosted.ts +31 -68
- package/src/index.ts +1 -0
- package/src/internal/artifacts-store.ts +131 -25
- package/src/internal/cursor/cursor-api-transport.ts +73 -0
- package/src/internal/cursor/hosted-store-secrets.ts +35 -0
- package/src/internal/cursor/store-api-client.ts +360 -0
- package/src/internal/deploy-manifest.ts +15 -0
- package/src/internal/reminder-runner.ts +30 -5
- package/src/internal/session-engine.ts +17 -22
- package/src/internal/store-api-protocol.ts +222 -0
- package/src/memory.ts +240 -93
- package/src/reminders.ts +1 -0
- package/src/types.ts +31 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DjNBgxTF.js +0 -1
- package/dist/docs/assets/chunks/channel.BNF8VK-B.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.iViHzfrK.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.iViHzfrK.js +0 -1
- package/dist/docs/assets/chunks/clone.CJoR2BBM.js +0 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.nV8Nl-td.js +0 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.Do4PwzeN.js +0 -162
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-call wire contract for the factory store API:
|
|
3
|
+
* `POST /internal/agentsdk/store/api/<call>` carrying a versioned JSON
|
|
4
|
+
* envelope `{ v, call, payload }`. This is the lane where the **server** owns
|
|
5
|
+
* the storage pattern (journal sharding, rotation, compaction), so a
|
|
6
|
+
* pathological pattern is fixable in a backend deploy instead of a July
|
|
7
|
+
* release plus a rebake of every deployment. The verb lane (`/flush`,
|
|
8
|
+
* `/read`, `/list`) keeps carrying bulk presigned file IO.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately duplicated on the backend
|
|
11
|
+
* (`backend/server/src/factory/agent-sdk/agentsdkStoreApiProtocol.ts`): this
|
|
12
|
+
* package is published to npm, so the backend cannot depend on it and it
|
|
13
|
+
* cannot depend on backend packages. A backend drift test compares the two
|
|
14
|
+
* sources — same shape as the hosted-delivery protocol twins — which is why
|
|
15
|
+
* every exported constant here stays a single `export const NAME = <literal>;`
|
|
16
|
+
* line and every schema field stays on its own line.
|
|
17
|
+
*
|
|
18
|
+
* Evolution rules (the compat contract): additive optional fields only; no
|
|
19
|
+
* renames, no retypes, no semantic reuse of an old field; unknown fields are
|
|
20
|
+
* ignored, never rejected; removing a field means deprecating it in place
|
|
21
|
+
* forever. A breaking change means a new `v`, and the server keeps serving
|
|
22
|
+
* every previous `v`.
|
|
23
|
+
*/
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
export const STORE_API_PROTOCOL_VERSION = 1;
|
|
26
|
+
/** Path family on the factory surface; one segment per call. */
|
|
27
|
+
export const STORE_API_PATH = "/internal/agentsdk/store/api";
|
|
28
|
+
export const STORE_API_MEMORY_APPEND_CALL = "memory.append";
|
|
29
|
+
/**
|
|
30
|
+
* Semantic cap on one serialized memory record. Text fields are truncated
|
|
31
|
+
* client-side (`memoryHook` default 2000 chars per field), so a well-formed
|
|
32
|
+
* record is a few KiB; 16 KiB is generous headroom, not a target. The cap is
|
|
33
|
+
* part of the wire contract but rides the layers, not the schema: the
|
|
34
|
+
* backend's store controller enforces it as a typed 4xx, and the store API
|
|
35
|
+
* client (`cursor/store-api-client.ts`) pre-checks it before spending a
|
|
36
|
+
* doomed request.
|
|
37
|
+
*/
|
|
38
|
+
export const MEMORY_APPEND_MAX_RECORD_BYTES = 16 * 1024;
|
|
39
|
+
/** Serialized size of a record, as counted against the byte cap. */
|
|
40
|
+
export function serializedRecordBytes(record) {
|
|
41
|
+
return new TextEncoder().encode(JSON.stringify(record)).byteLength;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wire schema for {@link TurnMemoryRecordWire}. Per-field caps are
|
|
45
|
+
* deliberately absent: the client truncates text at authoring time and
|
|
46
|
+
* {@link MEMORY_APPEND_MAX_RECORD_BYTES} bounds the whole record.
|
|
47
|
+
*/
|
|
48
|
+
export const turnMemoryRecordWireSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
at: z.string().min(1),
|
|
51
|
+
sessionId: z.string().min(1),
|
|
52
|
+
channelId: z.string().min(1),
|
|
53
|
+
status: z.enum(["completed", "failed"]),
|
|
54
|
+
title: z.string().optional(),
|
|
55
|
+
sdkAgentId: z.string().optional(),
|
|
56
|
+
userMessage: z.string().optional(),
|
|
57
|
+
result: z.string().optional(),
|
|
58
|
+
usage: z.object({}).passthrough().optional(),
|
|
59
|
+
})
|
|
60
|
+
.passthrough();
|
|
61
|
+
/**
|
|
62
|
+
* `memory.append` payload shape. The agent name becomes a store key segment
|
|
63
|
+
* on the server, so it must be exactly one segment. Unknown payload fields
|
|
64
|
+
* are stripped (= ignored), per the evolution rules. The byte cap is
|
|
65
|
+
* semantics, not shape — the backend's store controller owns it.
|
|
66
|
+
*/
|
|
67
|
+
export const memoryAppendPayloadSchema = z.object({
|
|
68
|
+
agent: z
|
|
69
|
+
.string()
|
|
70
|
+
.min(1)
|
|
71
|
+
.max(256)
|
|
72
|
+
.refine(name => !name.includes("/") &&
|
|
73
|
+
!name.includes("\\") &&
|
|
74
|
+
name !== "." &&
|
|
75
|
+
name !== "..", { message: "agent must be a single store key segment" }),
|
|
76
|
+
record: turnMemoryRecordWireSchema,
|
|
77
|
+
});
|
|
78
|
+
/** The whole request body. Unknown envelope fields are stripped (= ignored). */
|
|
79
|
+
export const memoryAppendEnvelopeSchema = z.object({
|
|
80
|
+
v: z.literal(STORE_API_PROTOCOL_VERSION),
|
|
81
|
+
call: z.literal(STORE_API_MEMORY_APPEND_CALL),
|
|
82
|
+
payload: memoryAppendPayloadSchema,
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Success body. Failures answer the store surface's standard error JSON
|
|
86
|
+
* (`{ code, error }`) with a matching HTTP status.
|
|
87
|
+
*/
|
|
88
|
+
export const memoryAppendResponseSchema = z.object({
|
|
89
|
+
v: z.literal(STORE_API_PROTOCOL_VERSION),
|
|
90
|
+
ok: z.literal(true),
|
|
91
|
+
});
|
|
92
|
+
export const STORE_API_MEMORY_READ_CALL = "memory.read";
|
|
93
|
+
/**
|
|
94
|
+
* `memory.read` payload shape — the same single-segment agent name rule as
|
|
95
|
+
* `memory.append`, since the name addresses the same journal key family.
|
|
96
|
+
*/
|
|
97
|
+
export const memoryReadPayloadSchema = z.object({
|
|
98
|
+
agent: z
|
|
99
|
+
.string()
|
|
100
|
+
.min(1)
|
|
101
|
+
.max(256)
|
|
102
|
+
.refine(name => !name.includes("/") &&
|
|
103
|
+
!name.includes("\\") &&
|
|
104
|
+
name !== "." &&
|
|
105
|
+
name !== "..", { message: "agent must be a single store key segment" }),
|
|
106
|
+
});
|
|
107
|
+
/** The whole request body. Unknown envelope fields are stripped (= ignored). */
|
|
108
|
+
export const memoryReadEnvelopeSchema = z.object({
|
|
109
|
+
v: z.literal(STORE_API_PROTOCOL_VERSION),
|
|
110
|
+
call: z.literal(STORE_API_MEMORY_READ_CALL),
|
|
111
|
+
payload: memoryReadPayloadSchema,
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Success body. Failures answer the store surface's standard error JSON
|
|
115
|
+
* (`{ code, error }`) with a matching HTTP status.
|
|
116
|
+
*/
|
|
117
|
+
export const memoryReadResponseSchema = z.object({
|
|
118
|
+
v: z.literal(STORE_API_PROTOCOL_VERSION),
|
|
119
|
+
ok: z.literal(true),
|
|
120
|
+
journal: z
|
|
121
|
+
.object({
|
|
122
|
+
url: z.string().min(1),
|
|
123
|
+
expiresAt: z.string().min(1),
|
|
124
|
+
})
|
|
125
|
+
.nullable(),
|
|
126
|
+
});
|
package/dist/memory.d.ts
CHANGED
|
@@ -18,11 +18,16 @@
|
|
|
18
18
|
*
|
|
19
19
|
* On Cursor-managed hosting the default is {@link agentStoreMemoryBackend}:
|
|
20
20
|
* the journal lives on the deployment's Agent Store (survives deploys,
|
|
21
|
-
* visible on cloud VMs under the store mount)
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* visible on cloud VMs under the store mount), written exclusively through
|
|
22
|
+
* the `memory.append` domain call — the server owns the journal pattern —
|
|
23
|
+
* and mirrored to `<stateRoot>/memory/journal.jsonl` (hydrated per session
|
|
24
|
+
* via `memory.read`, appended locally per turn) so the workspace symlink
|
|
25
|
+
* read path keeps working for local-runtime turns. The
|
|
26
|
+
* `factory-api-v1` pin file exists only on hosted pods — plain local dev
|
|
27
|
+
* runs {@link fileMemoryBackend} and never sees the hosted lane.
|
|
24
28
|
*/
|
|
25
|
-
import { type
|
|
29
|
+
import { type CursorHostedFilesOptions } from "./files-backends/cursor-hosted.js";
|
|
30
|
+
import { type StoreApiClient } from "./internal/cursor/store-api-client.js";
|
|
26
31
|
import type { HookDefinition, TurnUsage } from "./types.js";
|
|
27
32
|
/** Name of the shared memory directory under the agent state root. */
|
|
28
33
|
export declare const MEMORY_DIR_NAME = "memory";
|
|
@@ -49,6 +54,16 @@ export interface MemoryBackend {
|
|
|
49
54
|
stateRoot: string;
|
|
50
55
|
agentName: string;
|
|
51
56
|
}): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Optional session-start hook: bring the backend's local read path up to
|
|
59
|
+
* date before the session's first turn reads it. A freshness upgrade, not
|
|
60
|
+
* a correctness gate — implementations soft-fail and keep whatever read
|
|
61
|
+
* state already exists.
|
|
62
|
+
*/
|
|
63
|
+
prepareSession?(ctx: {
|
|
64
|
+
stateRoot: string;
|
|
65
|
+
agentName: string;
|
|
66
|
+
}): Promise<void>;
|
|
52
67
|
}
|
|
53
68
|
export interface FileMemoryBackendOptions {
|
|
54
69
|
/**
|
|
@@ -64,20 +79,40 @@ export interface FileMemoryBackendOptions {
|
|
|
64
79
|
*/
|
|
65
80
|
export declare function fileMemoryBackend(options?: FileMemoryBackendOptions): MemoryBackend;
|
|
66
81
|
export interface AgentStoreMemoryBackendOptions {
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Local mirror rotation threshold, same meaning as
|
|
84
|
+
* {@link FileMemoryBackendOptions}. The durable journal's rotation is the
|
|
85
|
+
* server's (`memory.append` controller), not this.
|
|
86
|
+
*/
|
|
70
87
|
maxJournalBytes?: number;
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Cursor-hosted transport overrides (tests): base URL, credential, store
|
|
90
|
+
* source id, protocol pin, fetch. Feeds the default
|
|
91
|
+
* {@link storeApiClient} and the lane-availability check.
|
|
92
|
+
*/
|
|
93
|
+
hosted?: CursorHostedFilesOptions;
|
|
94
|
+
/**
|
|
95
|
+
* Domain-lane client override (tests). Defaults to
|
|
96
|
+
* {@link createStoreApiClient} over {@link hosted} — the module that owns
|
|
97
|
+
* the `memory.append` / `memory.read` transport, typed errors, and
|
|
98
|
+
* bounded backoff.
|
|
99
|
+
*/
|
|
100
|
+
storeApiClient?: StoreApiClient;
|
|
75
101
|
}
|
|
76
102
|
/**
|
|
77
103
|
* Journal on the deployment's Agent Store — durable across deploys, visible
|
|
78
|
-
* on cloud VMs under the store mount
|
|
79
|
-
*
|
|
80
|
-
*
|
|
104
|
+
* on cloud VMs under the store mount, written exclusively through the
|
|
105
|
+
* `memory.append` domain call: the server owns the journal's storage
|
|
106
|
+
* pattern (Redis-buffered flush, rotation), so a pathological pattern is
|
|
107
|
+
* fixable in a backend deploy instead of a July release plus a rebake.
|
|
108
|
+
*
|
|
109
|
+
* There is no client-composed fallback. The read-modify-write CAS lane that
|
|
110
|
+
* predated the domain call (and caused the 2026-09-18 Agent Store read
|
|
111
|
+
* storm) was removed in 0.2.1 together with v2 hosting binding the
|
|
112
|
+
* `factory-api-v1` pin unconditionally and refusing artifacts frozen on an
|
|
113
|
+
* older July. A hosted pod without the pin is therefore a misconfiguration
|
|
114
|
+
* (a server predating the pin change, or a local env naming a hosted store
|
|
115
|
+
* without one): records are dropped loudly, never written client-side.
|
|
81
116
|
*/
|
|
82
117
|
export declare function agentStoreMemoryBackend(options?: AgentStoreMemoryBackendOptions): MemoryBackend;
|
|
83
118
|
export interface MemoryHookOptions {
|
package/dist/memory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,OAAO,EACL,KAAK,wBAAwB,EAE9B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAe,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzE,sEAAsE;AACtE,eAAO,MAAM,eAAe,WAAW,CAAC;AAExC,gDAAgD;AAChD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+CAA+C;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,UAAU,CACR,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,CAAC,GAAG,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,aAAa,CAgCf;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,8BAAmC,GAC3C,aAAa,CAiMf;AAqBD,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CA4G1E"}
|
package/dist/memory.js
CHANGED
|
@@ -18,9 +18,13 @@
|
|
|
18
18
|
*
|
|
19
19
|
* On Cursor-managed hosting the default is {@link agentStoreMemoryBackend}:
|
|
20
20
|
* the journal lives on the deployment's Agent Store (survives deploys,
|
|
21
|
-
* visible on cloud VMs under the store mount)
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* visible on cloud VMs under the store mount), written exclusively through
|
|
22
|
+
* the `memory.append` domain call — the server owns the journal pattern —
|
|
23
|
+
* and mirrored to `<stateRoot>/memory/journal.jsonl` (hydrated per session
|
|
24
|
+
* via `memory.read`, appended locally per turn) so the workspace symlink
|
|
25
|
+
* read path keeps working for local-runtime turns. The
|
|
26
|
+
* `factory-api-v1` pin file exists only on hosted pods — plain local dev
|
|
27
|
+
* runs {@link fileMemoryBackend} and never sees the hosted lane.
|
|
24
28
|
*/
|
|
25
29
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
30
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
@@ -34,9 +38,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
34
38
|
import { randomUUID } from "node:crypto";
|
|
35
39
|
import { appendFile, mkdir, rename, stat, writeFile } from "node:fs/promises";
|
|
36
40
|
import { join } from "node:path";
|
|
37
|
-
import { agentStoreKeys
|
|
38
|
-
import {
|
|
41
|
+
import { agentStoreKeys } from "./files.js";
|
|
42
|
+
import { isCursorHostedFilesAvailable, } from "./files-backends/cursor-hosted.js";
|
|
39
43
|
import { defineHook } from "./hooks.js";
|
|
44
|
+
import { createStoreApiClient, isStoreApiLaneEnabled, } from "./internal/cursor/store-api-client.js";
|
|
40
45
|
/** Name of the shared memory directory under the agent state root. */
|
|
41
46
|
export const MEMORY_DIR_NAME = "memory";
|
|
42
47
|
/**
|
|
@@ -80,93 +85,189 @@ export function fileMemoryBackend(options = {}) {
|
|
|
80
85
|
},
|
|
81
86
|
};
|
|
82
87
|
}
|
|
83
|
-
/**
|
|
84
|
-
* CAS attempts per append. The journal is one deployment-wide key written by
|
|
85
|
-
* every engine pod, so under concurrent turn traffic the etag precondition
|
|
86
|
-
* loses races routinely; each retry needs headroom to land.
|
|
87
|
-
*/
|
|
88
|
-
const MEMORY_CAS_MAX_ATTEMPTS = 5;
|
|
89
|
-
/**
|
|
90
|
-
* Jittered exponential backoff between CAS attempts. Retrying immediately is
|
|
91
|
-
* a fleet-wide stampede: every losing pod re-GETs the full journal and
|
|
92
|
-
* re-PUTs in lockstep, which multiplied reads ~65x on one deployment during
|
|
93
|
-
* the 2026-09-18 Agent Store read storm and tripped the store's
|
|
94
|
-
* per-service-account read limit (every pod shares the deployment
|
|
95
|
-
* credential). Jitter desynchronizes the writers so most retries land on the
|
|
96
|
-
* first or second attempt.
|
|
97
|
-
*/
|
|
98
|
-
function memoryCasBackoffMs(attempt, random) {
|
|
99
|
-
return 100 * Math.pow(2, attempt) * (0.5 + random());
|
|
100
|
-
}
|
|
101
88
|
/**
|
|
102
89
|
* Journal on the deployment's Agent Store — durable across deploys, visible
|
|
103
|
-
* on cloud VMs under the store mount
|
|
104
|
-
*
|
|
105
|
-
*
|
|
90
|
+
* on cloud VMs under the store mount, written exclusively through the
|
|
91
|
+
* `memory.append` domain call: the server owns the journal's storage
|
|
92
|
+
* pattern (Redis-buffered flush, rotation), so a pathological pattern is
|
|
93
|
+
* fixable in a backend deploy instead of a July release plus a rebake.
|
|
94
|
+
*
|
|
95
|
+
* There is no client-composed fallback. The read-modify-write CAS lane that
|
|
96
|
+
* predated the domain call (and caused the 2026-09-18 Agent Store read
|
|
97
|
+
* storm) was removed in 0.2.1 together with v2 hosting binding the
|
|
98
|
+
* `factory-api-v1` pin unconditionally and refusing artifacts frozen on an
|
|
99
|
+
* older July. A hosted pod without the pin is therefore a misconfiguration
|
|
100
|
+
* (a server predating the pin change, or a local env naming a hosted store
|
|
101
|
+
* without one): records are dropped loudly, never written client-side.
|
|
106
102
|
*/
|
|
107
103
|
export function agentStoreMemoryBackend(options = {}) {
|
|
108
|
-
var _a, _b, _c
|
|
109
|
-
const
|
|
104
|
+
var _a, _b, _c;
|
|
105
|
+
const hosted = (_a = options.hosted) !== null && _a !== void 0 ? _a : {};
|
|
110
106
|
const maxJournalBytes = (_b = options.maxJournalBytes) !== null && _b !== void 0 ? _b : 5 * 1024 * 1024;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
// a failed append must not poison the chain for later turns. The chain
|
|
115
|
-
// serializes appends within this pod only — pods race each other on the
|
|
116
|
-
// shared key, which is what the CAS retry (with backoff) absorbs.
|
|
107
|
+
// Mirror work is chained per journal key (same shape as
|
|
108
|
+
// fileMemoryBackend): hydration and appends on one agent's mirror never
|
|
109
|
+
// interleave, and a failed step must not poison the chain for later turns.
|
|
117
110
|
const appendChains = new Map();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
111
|
+
// Transport, envelope, typed errors, and bounded backoff all live in the
|
|
112
|
+
// client; this module only decides which lane an append takes.
|
|
113
|
+
const storeApiClient = (_c = options.storeApiClient) !== null && _c !== void 0 ? _c : createStoreApiClient(hosted);
|
|
114
|
+
/**
|
|
115
|
+
* The domain lane still owes local-runtime sessions their read path:
|
|
116
|
+
* cloud-runtime turns read the journal through the store mount, but
|
|
117
|
+
* local-runtime turns on hosted pods (the default `runtime: "local"`)
|
|
118
|
+
* read `<stateRoot>/memory/journal.jsonl` via the workspace symlink, and
|
|
119
|
+
* on hosting the only writer of that file is this backend. So the lane
|
|
120
|
+
* keeps the mirror live without reintroducing the CAS read pattern:
|
|
121
|
+
* hydrate it once from the store when the file is absent (a fresh pod
|
|
122
|
+
* after a deploy), then append each successfully posted record locally,
|
|
123
|
+
* rotating at {@link maxJournalBytes} like {@link fileMemoryBackend}.
|
|
124
|
+
* Server-buffered records not yet flushed when a pod hydrates are the
|
|
125
|
+
* lane's documented staleness window (~30 s / 64 KiB), and other pods'
|
|
126
|
+
* appends surface on the next hydration — advisory memory, same envelope
|
|
127
|
+
* as the server side.
|
|
128
|
+
*/
|
|
129
|
+
const hydratedMirrors = new Set();
|
|
130
|
+
let mirrorRotationSeq = 0;
|
|
131
|
+
const maintainLocalMirror = (record, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
var _a;
|
|
133
|
+
var _b;
|
|
134
|
+
const dir = join(ctx.stateRoot, MEMORY_DIR_NAME);
|
|
135
|
+
const path = join(dir, "journal.jsonl");
|
|
136
|
+
if (!hydratedMirrors.has(path)) {
|
|
137
|
+
// Appends are chained per journal key, so this guard never races
|
|
138
|
+
// itself for one agent. Backstop for a session that appended before
|
|
139
|
+
// any prepareSession hydration ran (e.g. a custom hook ordering).
|
|
140
|
+
hydratedMirrors.add(path);
|
|
141
|
+
const existing = yield stat(path).catch(() => undefined);
|
|
142
|
+
if (existing === undefined) {
|
|
143
|
+
const content = yield storeApiClient.memoryReadContent({
|
|
144
|
+
agent: ctx.agentName,
|
|
144
145
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (error instanceof FileConflictError &&
|
|
148
|
-
attempt < MEMORY_CAS_MAX_ATTEMPTS - 1) {
|
|
149
|
-
yield sleep(memoryCasBackoffMs(attempt, random));
|
|
150
|
-
continue;
|
|
146
|
+
if (content !== undefined) {
|
|
147
|
+
yield mirrorJournalLocally(ctx.stateRoot, content);
|
|
151
148
|
}
|
|
152
|
-
throw error;
|
|
153
|
-
}
|
|
154
|
-
yield mirrorJournalLocally(ctx.stateRoot, next);
|
|
155
|
-
if (rotatedStamp !== undefined) {
|
|
156
|
-
// Rotated segments sit beside the live journal locally too, so the
|
|
157
|
-
// workspace symlink read path keeps pre-rotation history.
|
|
158
|
-
yield writeFile(join(ctx.stateRoot, MEMORY_DIR_NAME, `journal-${rotatedStamp}.jsonl`), currentBody);
|
|
159
149
|
}
|
|
150
|
+
}
|
|
151
|
+
yield mkdir(dir, { recursive: true });
|
|
152
|
+
const size = (_b = (_a = (yield stat(path).catch(() => undefined))) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0;
|
|
153
|
+
if (size >= maxJournalBytes) {
|
|
154
|
+
mirrorRotationSeq += 1;
|
|
155
|
+
yield rename(path, join(dir, `journal-${Date.now()}-${mirrorRotationSeq}.jsonl`));
|
|
156
|
+
}
|
|
157
|
+
yield appendFile(path, `${JSON.stringify(record)}\n`, "utf8");
|
|
158
|
+
});
|
|
159
|
+
/**
|
|
160
|
+
* One `memory.append` domain call; the server appends to the server-owned
|
|
161
|
+
* journal. On success the record is also appended to the local mirror so
|
|
162
|
+
* the workspace symlink read path stays live (see
|
|
163
|
+
* {@link maintainLocalMirror}).
|
|
164
|
+
*
|
|
165
|
+
* Failure policy: log and give up for this record. Memory is advisory and
|
|
166
|
+
* the hook runner treats an append failure as non-fatal to the turn;
|
|
167
|
+
* falling back to the CAS path per-record would reintroduce the write
|
|
168
|
+
* storm under exactly the server brownout that makes this call fail. A
|
|
169
|
+
* failed POST also skips the mirror — the mirror must never show a record
|
|
170
|
+
* the durable journal will not have.
|
|
171
|
+
*/
|
|
172
|
+
const appendViaStoreApi = (record, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
try {
|
|
174
|
+
yield storeApiClient.memoryAppend({ agent: ctx.agentName, record });
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.warn(`[agent-serve] memory.append dropped one record for "${ctx.agentName}": ${error instanceof Error ? error.message : String(error)}`);
|
|
160
178
|
return;
|
|
161
179
|
}
|
|
180
|
+
try {
|
|
181
|
+
yield maintainLocalMirror(record, ctx);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
console.warn(`[agent-serve] memory mirror update failed for "${ctx.agentName}" (journal record is durable): ${error instanceof Error ? error.message : String(error)}`);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
/**
|
|
188
|
+
* Session-start hydration: one `memory.read` (flush-on-read server-side,
|
|
189
|
+
* so the pending buffer — including a burst another pod appended — lands
|
|
190
|
+
* first) and the mirror is rewritten to global state; the session then
|
|
191
|
+
* reads everything appended before it started. Soft-fail: a failed
|
|
192
|
+
* hydration keeps the existing mirror.
|
|
193
|
+
*/
|
|
194
|
+
const hydrateMirror = (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
var _a;
|
|
196
|
+
var _b;
|
|
197
|
+
const path = join(ctx.stateRoot, MEMORY_DIR_NAME, "journal.jsonl");
|
|
198
|
+
try {
|
|
199
|
+
const content = yield storeApiClient.memoryReadContent({
|
|
200
|
+
agent: ctx.agentName,
|
|
201
|
+
});
|
|
202
|
+
if (content !== undefined) {
|
|
203
|
+
// Never regress the read path: every mirrored record was
|
|
204
|
+
// server-acked before it was written locally, so a download SHORTER
|
|
205
|
+
// than the mirror means the server's flush-on-read was degraded (a
|
|
206
|
+
// busy lock, a Redis outage — the acked tail is still buffered) or
|
|
207
|
+
// the journal rotated. Keeping the richer mirror loses nothing;
|
|
208
|
+
// rewriting would hide records the agent has already seen.
|
|
209
|
+
const mirroredBytes = (_b = (_a = (yield stat(path).catch(() => undefined))) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0;
|
|
210
|
+
if (content.byteLength >= mirroredBytes) {
|
|
211
|
+
yield mirrorJournalLocally(ctx.stateRoot, content);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Either way the read answered: skip the append path's absent-file
|
|
215
|
+
// hydration for the rest of this process.
|
|
216
|
+
hydratedMirrors.add(path);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.warn(`[agent-serve] memory hydration failed for "${ctx.agentName}" (keeping the existing mirror): ${error instanceof Error ? error.message : String(error)}`);
|
|
220
|
+
}
|
|
162
221
|
});
|
|
222
|
+
/**
|
|
223
|
+
* Hosted pods carry the pin and a provisioned store by construction (v2
|
|
224
|
+
* hosting binds `factory-api-v1` unconditionally and refuses pre-0.2.1
|
|
225
|
+
* artifacts). Reaching this without them means a misconfigured
|
|
226
|
+
* environment; there is no client-side write to fall back to.
|
|
227
|
+
*/
|
|
228
|
+
const refuseMisconfiguredLane = (agentName, verb) => {
|
|
229
|
+
console.error(`[agent-serve] memory ${verb} for "${agentName}" dropped: the store API lane is unavailable (missing/foreign AGENT_SERVE_STORE_PROTOCOL pin or no provisioned store). This July has no client-side journal fallback; fix the hosting pin.`);
|
|
230
|
+
};
|
|
163
231
|
return {
|
|
232
|
+
prepareSession(ctx) {
|
|
233
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
+
var _a;
|
|
235
|
+
// Chained on the same per-journal key as appends, so hydration never
|
|
236
|
+
// interleaves with an append's mirror write.
|
|
237
|
+
const key = agentStoreKeys.memoryJournal(ctx.agentName);
|
|
238
|
+
const prior = (_a = appendChains.get(key)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
239
|
+
const next = prior
|
|
240
|
+
.catch(() => { })
|
|
241
|
+
.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
242
|
+
if (!isStoreApiLaneEnabled(hosted)) {
|
|
243
|
+
refuseMisconfiguredLane(ctx.agentName, "hydration");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
yield hydrateMirror(ctx);
|
|
247
|
+
}));
|
|
248
|
+
appendChains.set(key, next);
|
|
249
|
+
yield next;
|
|
250
|
+
});
|
|
251
|
+
},
|
|
164
252
|
appendTurn(record, ctx) {
|
|
165
253
|
return __awaiter(this, void 0, void 0, function* () {
|
|
166
254
|
var _a;
|
|
255
|
+
// Soft-fail by construction (appendViaStoreApi never throws), but
|
|
256
|
+
// still chained per journal key: the local mirror's hydrate-then-
|
|
257
|
+
// append must not interleave with itself. Lane availability is
|
|
258
|
+
// re-resolved lazily per append, so a pin bound after serve start
|
|
259
|
+
// takes effect without a restart.
|
|
167
260
|
const key = agentStoreKeys.memoryJournal(ctx.agentName);
|
|
168
261
|
const prior = (_a = appendChains.get(key)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
169
|
-
const next = prior
|
|
262
|
+
const next = prior
|
|
263
|
+
.catch(() => { })
|
|
264
|
+
.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
265
|
+
if (!isStoreApiLaneEnabled(hosted)) {
|
|
266
|
+
refuseMisconfiguredLane(ctx.agentName, "append");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
yield appendViaStoreApi(record, ctx);
|
|
270
|
+
}));
|
|
170
271
|
appendChains.set(key, next);
|
|
171
272
|
yield next;
|
|
172
273
|
});
|
|
@@ -254,6 +355,24 @@ export function memoryHook(options = {}) {
|
|
|
254
355
|
};
|
|
255
356
|
return defineHook({
|
|
256
357
|
events: {
|
|
358
|
+
"session.started"(_event, ctx) {
|
|
359
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
360
|
+
var _a;
|
|
361
|
+
// Bring the mirror (the workspace symlink read path) up to date
|
|
362
|
+
// before the session's first turn reads it. Backends without a
|
|
363
|
+
// prepareSession (plain files) have nothing to freshen. Soft-fail:
|
|
364
|
+
// hydration is a freshness upgrade, never a turn blocker.
|
|
365
|
+
try {
|
|
366
|
+
yield (_a = backend.prepareSession) === null || _a === void 0 ? void 0 : _a.call(backend, {
|
|
367
|
+
stateRoot: ctx.stateRoot,
|
|
368
|
+
agentName: ctx.agent.name,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
console.warn(`[agent-serve] memory hydration at session start failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
},
|
|
257
376
|
"message.received"(event, ctx) {
|
|
258
377
|
return __awaiter(this, void 0, void 0, function* () {
|
|
259
378
|
pendingMessages.set(pendingKey(ctx, event.turnId), truncate(event.data.text, maxTextLength));
|