@moltzap/client 2026.511.1 → 2026.519.0
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/auth.d.ts +12 -6
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +67 -29
- package/dist/auth.js.map +1 -1
- package/dist/auth.test.js +113 -76
- package/dist/auth.test.js.map +1 -1
- package/dist/channel-base/format-cross-conv.d.ts +43 -0
- package/dist/channel-base/format-cross-conv.d.ts.map +1 -0
- package/dist/channel-base/format-cross-conv.js +69 -0
- package/dist/channel-base/format-cross-conv.js.map +1 -0
- package/dist/channel-base/format-group-block.d.ts +43 -0
- package/dist/channel-base/format-group-block.d.ts.map +1 -0
- package/dist/channel-base/format-group-block.js +59 -0
- package/dist/channel-base/format-group-block.js.map +1 -0
- package/dist/channel-base/index.d.ts +21 -0
- package/dist/channel-base/index.d.ts.map +1 -0
- package/dist/channel-base/index.js +24 -0
- package/dist/channel-base/index.js.map +1 -0
- package/dist/channel-base/lease-guard.d.ts +30 -0
- package/dist/channel-base/lease-guard.d.ts.map +1 -0
- package/dist/channel-base/lease-guard.js +40 -0
- package/dist/channel-base/lease-guard.js.map +1 -0
- package/dist/channel-base/lease-store.d.ts +30 -0
- package/dist/channel-base/lease-store.d.ts.map +1 -0
- package/dist/channel-base/lease-store.js +58 -0
- package/dist/channel-base/lease-store.js.map +1 -0
- package/dist/channel-base/lease.d.ts +94 -0
- package/dist/channel-base/lease.d.ts.map +1 -0
- package/dist/channel-base/lease.js +110 -0
- package/dist/channel-base/lease.js.map +1 -0
- package/dist/channel-core-context.test.d.ts +2 -0
- package/dist/channel-core-context.test.d.ts.map +1 -0
- package/dist/channel-core-context.test.js +334 -0
- package/dist/channel-core-context.test.js.map +1 -0
- package/dist/channel-core-dispatch.test.d.ts +2 -0
- package/dist/channel-core-dispatch.test.d.ts.map +1 -0
- package/dist/channel-core-dispatch.test.js +550 -0
- package/dist/channel-core-dispatch.test.js.map +1 -0
- package/dist/channel-core-enrichment.d.ts +8 -0
- package/dist/channel-core-enrichment.d.ts.map +1 -0
- package/dist/channel-core-enrichment.js +134 -0
- package/dist/channel-core-enrichment.js.map +1 -0
- package/dist/channel-core-errors.d.ts +20 -0
- package/dist/channel-core-errors.d.ts.map +1 -0
- package/dist/channel-core-errors.js +12 -0
- package/dist/channel-core-errors.js.map +1 -0
- package/dist/channel-core-test-support.d.ts +48 -0
- package/dist/channel-core-test-support.d.ts.map +1 -0
- package/dist/channel-core-test-support.js +111 -0
- package/dist/channel-core-test-support.js.map +1 -0
- package/dist/channel-core.d.ts +30 -7
- package/dist/channel-core.d.ts.map +1 -1
- package/dist/channel-core.js +214 -237
- package/dist/channel-core.js.map +1 -1
- package/dist/channel-core.test.js +216 -1082
- package/dist/channel-core.test.js.map +1 -1
- package/dist/cli/__tests__/cli-multi-agent.int.test.js +129 -23
- package/dist/cli/__tests__/cli-multi-agent.int.test.js.map +1 -1
- package/dist/cli/commands/conversations-v2.test.js +64 -54
- package/dist/cli/commands/conversations-v2.test.js.map +1 -1
- package/dist/cli/commands/conversations.d.ts +5 -5
- package/dist/cli/commands/conversations.d.ts.map +1 -1
- package/dist/cli/commands/conversations.js +43 -36
- package/dist/cli/commands/conversations.js.map +1 -1
- package/dist/cli/commands/messages.d.ts +4 -4
- package/dist/cli/commands/messages.d.ts.map +1 -1
- package/dist/cli/commands/messages.js +4 -4
- package/dist/cli/commands/messages.js.map +1 -1
- package/dist/cli/commands/messages.test.js +61 -49
- package/dist/cli/commands/messages.test.js.map +1 -1
- package/dist/cli/commands/ping.d.ts +0 -9
- package/dist/cli/commands/ping.d.ts.map +1 -1
- package/dist/cli/commands/ping.js +1 -1
- package/dist/cli/commands/ping.js.map +1 -1
- package/dist/cli/commands/presence.d.ts +1 -1
- package/dist/cli/commands/presence.js +1 -1
- package/dist/cli/commands/register.d.ts +2 -2
- package/dist/cli/commands/register.d.ts.map +1 -1
- package/dist/cli/commands/register.js +98 -77
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/register.test.js +63 -39
- package/dist/cli/commands/register.test.js.map +1 -1
- package/dist/cli/commands/send.d.ts +6 -6
- package/dist/cli/commands/send.js +6 -6
- package/dist/cli/commands/send.test.js +32 -28
- package/dist/cli/commands/send.test.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +3 -4
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/config.d.ts +0 -2
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +86 -76
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/config.test.js +80 -42
- package/dist/cli/config.test.js.map +1 -1
- package/dist/cli/index.d.ts +1 -16
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +50 -113
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/process-argv.d.ts +2 -0
- package/dist/cli/process-argv.d.ts.map +1 -0
- package/dist/cli/process-argv.js +2 -0
- package/dist/cli/process-argv.js.map +1 -0
- package/dist/cli/profile.d.ts +3 -10
- package/dist/cli/profile.d.ts.map +1 -1
- package/dist/cli/profile.js +105 -104
- package/dist/cli/profile.js.map +1 -1
- package/dist/cli/profile.test.js +338 -232
- package/dist/cli/profile.test.js.map +1 -1
- package/dist/cli/runtime.d.ts +5 -17
- package/dist/cli/runtime.d.ts.map +1 -1
- package/dist/cli/runtime.js +6 -59
- package/dist/cli/runtime.js.map +1 -1
- package/dist/cli/socket-client.d.ts +10 -15
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +74 -89
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/cli/transport.d.ts +9 -5
- package/dist/cli/transport.d.ts.map +1 -1
- package/dist/cli/transport.js +45 -42
- package/dist/cli/transport.js.map +1 -1
- package/dist/cli/transport.test.js +227 -133
- package/dist/cli/transport.test.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/local-daemon-rpc.d.ts +13 -0
- package/dist/local-daemon-rpc.d.ts.map +1 -0
- package/dist/local-daemon-rpc.js +17 -0
- package/dist/local-daemon-rpc.js.map +1 -0
- package/dist/local-paths.d.ts +7 -0
- package/dist/local-paths.d.ts.map +1 -0
- package/dist/local-paths.js +20 -0
- package/dist/local-paths.js.map +1 -0
- package/dist/runtime/close-info.d.ts +3 -12
- package/dist/runtime/close-info.d.ts.map +1 -1
- package/dist/runtime/close-info.js +28 -39
- package/dist/runtime/close-info.js.map +1 -1
- package/dist/runtime/close-info.test.js +77 -70
- package/dist/runtime/close-info.test.js.map +1 -1
- package/dist/runtime/errors.test.js +42 -28
- package/dist/runtime/errors.test.js.map +1 -1
- package/dist/runtime/frame.d.ts +4 -31
- package/dist/runtime/frame.d.ts.map +1 -1
- package/dist/runtime/frame.js +7 -116
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/frame.test.js +41 -54
- package/dist/runtime/frame.test.js.map +1 -1
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +7 -11
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/local-history.d.ts +67 -0
- package/dist/runtime/local-history.d.ts.map +1 -0
- package/dist/runtime/local-history.js +77 -0
- package/dist/runtime/local-history.js.map +1 -0
- package/dist/runtime/local-service-commands.d.ts +19 -0
- package/dist/runtime/local-service-commands.d.ts.map +1 -1
- package/dist/runtime/local-service-commands.js +23 -0
- package/dist/runtime/local-service-commands.js.map +1 -1
- package/dist/runtime/local-socket-server.d.ts +20 -0
- package/dist/runtime/local-socket-server.d.ts.map +1 -0
- package/dist/runtime/local-socket-server.js +94 -0
- package/dist/runtime/local-socket-server.js.map +1 -0
- package/dist/runtime/refs.d.ts +1 -1
- package/dist/runtime/refs.js +1 -1
- package/dist/runtime/runtime.test.js +29 -21
- package/dist/runtime/runtime.test.js.map +1 -1
- package/dist/runtime/service-helpers.d.ts +32 -0
- package/dist/runtime/service-helpers.d.ts.map +1 -0
- package/dist/runtime/service-helpers.js +85 -0
- package/dist/runtime/service-helpers.js.map +1 -0
- package/dist/runtime/subscribers.d.ts +13 -17
- package/dist/runtime/subscribers.d.ts.map +1 -1
- package/dist/runtime/subscribers.js +37 -32
- package/dist/runtime/subscribers.js.map +1 -1
- package/dist/runtime/subscribers.test.js +139 -131
- package/dist/runtime/subscribers.test.js.map +1 -1
- package/dist/service-socket-path.test.d.ts +2 -0
- package/dist/service-socket-path.test.d.ts.map +1 -0
- package/dist/service-socket-path.test.js +87 -0
- package/dist/service-socket-path.test.js.map +1 -0
- package/dist/service.d.ts +23 -20
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +192 -340
- package/dist/service.js.map +1 -1
- package/dist/service.test.js +652 -653
- package/dist/service.test.js.map +1 -1
- package/dist/test/index.d.ts +30 -14
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +25 -21
- package/dist/test/index.js.map +1 -1
- package/dist/test-utils/channel-service-fixture.d.ts +1 -0
- package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
- package/dist/test-utils/channel-service-fixture.js +139 -98
- package/dist/test-utils/channel-service-fixture.js.map +1 -1
- package/dist/test-utils/conformance-adapter.d.ts.map +1 -1
- package/dist/test-utils/conformance-adapter.js +145 -124
- package/dist/test-utils/conformance-adapter.js.map +1 -1
- package/dist/test-utils/fake-service.d.ts.map +1 -1
- package/dist/test-utils/fake-service.js +3 -3
- package/dist/test-utils/fake-service.js.map +1 -1
- package/dist/test-utils/index.d.ts +25 -0
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +25 -8
- package/dist/test-utils/index.js.map +1 -1
- package/dist/ws-client-test-support.d.ts +180 -0
- package/dist/ws-client-test-support.d.ts.map +1 -0
- package/dist/ws-client-test-support.js +399 -0
- package/dist/ws-client-test-support.js.map +1 -0
- package/dist/ws-client.d.ts +59 -32
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +272 -321
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-client.test.js +388 -800
- package/dist/ws-client.test.js.map +1 -1
- package/package.json +18 -10
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base group-block helpers.
|
|
3
|
+
*
|
|
4
|
+
* Replaces nanoclaw's inline `formatGroupBlock` at
|
|
5
|
+
* `packages/nanoclaw-channel/src/channels/moltzap.ts → formatGroupBlock`. Also
|
|
6
|
+
* extracts the shared type-narrowing predicate (`getGroupFields`) consumed by
|
|
7
|
+
* openclaw (deriving `groupSubject` / `groupMembers` for the OpenClaw context)
|
|
8
|
+
* and nanoclaw (gating the inline group-block render).
|
|
9
|
+
*/
|
|
10
|
+
import { sanitizeForSystemReminder } from "../service.js";
|
|
11
|
+
const UNNAMED_GROUP_FALLBACK = "(unnamed)";
|
|
12
|
+
const NO_PARTICIPANTS_FALLBACK = "(none listed)";
|
|
13
|
+
/**
|
|
14
|
+
* Returns `{ name, participants }` when `meta?.type === "group"`; else `null`.
|
|
15
|
+
* Pure narrowing helper — callers use the non-null return to drive
|
|
16
|
+
* `formatGroupBlock` or per-channel field extraction (openclaw's
|
|
17
|
+
* `groupSubject` + `groupMembers`).
|
|
18
|
+
*/
|
|
19
|
+
export function getGroupFields(meta) {
|
|
20
|
+
if (meta?.type !== "group")
|
|
21
|
+
return null;
|
|
22
|
+
return { name: meta.name, participants: meta.participants };
|
|
23
|
+
}
|
|
24
|
+
function formatXmlSystemReminder(fields) {
|
|
25
|
+
const safeName = sanitizeForSystemReminder(fields.name ?? UNNAMED_GROUP_FALLBACK);
|
|
26
|
+
const safeParticipants = fields.participants.map(sanitizeForSystemReminder);
|
|
27
|
+
return [
|
|
28
|
+
"<system-reminder>",
|
|
29
|
+
"This is a group conversation.",
|
|
30
|
+
`Group name: ${safeName}`,
|
|
31
|
+
`Participants (${fields.participants.length}): ${safeParticipants.join(", ") || NO_PARTICIPANTS_FALLBACK}`,
|
|
32
|
+
"</system-reminder>",
|
|
33
|
+
].join("\n");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns the formatted group block.
|
|
37
|
+
*
|
|
38
|
+
* Markup variants:
|
|
39
|
+
* - `"xml-system-reminder"`: byte-identical to the pre-refactor nanoclaw
|
|
40
|
+
* output (a `<system-reminder>` block with group name + participants).
|
|
41
|
+
* - `"json-header"`: empty string. Openclaw does not render a group block;
|
|
42
|
+
* it consumes `getGroupFields` directly to derive its OpenClaw-side
|
|
43
|
+
* `groupSubject` / `groupMembers` fields. The empty-string return locks
|
|
44
|
+
* the "openclaw renders no group block" behavior as an explicit,
|
|
45
|
+
* fixtured output (see arch sub-issue #605 §3.5).
|
|
46
|
+
*
|
|
47
|
+
* Or pass a custom `formatter` callback.
|
|
48
|
+
*/
|
|
49
|
+
export function formatGroupBlock(fields, opts) {
|
|
50
|
+
if ("formatter" in opts)
|
|
51
|
+
return opts.formatter(fields);
|
|
52
|
+
switch (opts.markup) {
|
|
53
|
+
case "xml-system-reminder":
|
|
54
|
+
return formatXmlSystemReminder(fields);
|
|
55
|
+
case "json-header":
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=format-group-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-group-block.js","sourceRoot":"","sources":["../../src/channel-base/format-group-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAW1D,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAC3C,MAAM,wBAAwB,GAAG,eAAe,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,IAA0C;IAE1C,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,uBAAuB,CAAC,MAAmB;IAClD,MAAM,QAAQ,GAAG,yBAAyB,CACxC,MAAM,CAAC,IAAI,IAAI,sBAAsB,CACtC,CAAC;IACF,MAAM,gBAAgB,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAC5E,OAAO;QACL,mBAAmB;QACnB,+BAA+B;QAC/B,eAAe,QAAQ,EAAE;QACzB,iBAAiB,MAAM,CAAC,YAAY,CAAC,MAAM,MAAM,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,wBAAwB,EAAE;QAC1G,oBAAoB;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAmB,EACnB,IAE0C;IAE1C,IAAI,WAAW,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACvD,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,qBAAqB;YACxB,OAAO,uBAAuB,CAAC,MAAM,CAAC,CAAC;QACzC,KAAK,aAAa;YAChB,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `@moltzap/client/channel-base` — shared scaffolding for channel adapters.
|
|
3
|
+
*
|
|
4
|
+
* Used by `@moltzap/openclaw-channel`, `@moltzap/claude-code-channel`, and
|
|
5
|
+
* `@moltzap/nanoclaw-channel` to canonicalize the lease-lifecycle primitives,
|
|
6
|
+
* the `LeaseAlreadyConsumed` tagged error, and the markup-parameterized
|
|
7
|
+
* cross-conv + group-block formatters. Detail doc:
|
|
8
|
+
* `packages/client/docs/architecture/08-channel-base.md`. Spec: #597.
|
|
9
|
+
* Architect plan: #605.
|
|
10
|
+
*
|
|
11
|
+
* Note: this subpath is opt-in. Direct `@moltzap/client` consumers
|
|
12
|
+
* (server-core, runtimes, test-utils) see no API-surface change.
|
|
13
|
+
*/
|
|
14
|
+
export { LeaseAlreadyConsumed, projectLeaseInvalid, catchLeaseInvalid, type LeaseInvalidProjectionError, } from "./lease.js";
|
|
15
|
+
export { LeaseStore } from "./lease-store.js";
|
|
16
|
+
export { LeaseGuard } from "./lease-guard.js";
|
|
17
|
+
export { formatCrossConv, type CrossConvFormatter, type CrossConvMarkup, } from "./format-cross-conv.js";
|
|
18
|
+
export { formatGroupBlock, getGroupFields, type GroupFields, type GroupFormatter, } from "./format-group-block.js";
|
|
19
|
+
export { sanitizeForSystemReminder, type CrossConvMessage, } from "../service.js";
|
|
20
|
+
export { type EnrichedConversationMeta } from "../channel-core.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channel-base/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,2BAA2B,GACjC,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EACL,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,cAAc,GACpB,MAAM,yBAAyB,CAAC;AAKjC,OAAO,EACL,yBAAyB,EACzB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `@moltzap/client/channel-base` — shared scaffolding for channel adapters.
|
|
3
|
+
*
|
|
4
|
+
* Used by `@moltzap/openclaw-channel`, `@moltzap/claude-code-channel`, and
|
|
5
|
+
* `@moltzap/nanoclaw-channel` to canonicalize the lease-lifecycle primitives,
|
|
6
|
+
* the `LeaseAlreadyConsumed` tagged error, and the markup-parameterized
|
|
7
|
+
* cross-conv + group-block formatters. Detail doc:
|
|
8
|
+
* `packages/client/docs/architecture/08-channel-base.md`. Spec: #597.
|
|
9
|
+
* Architect plan: #605.
|
|
10
|
+
*
|
|
11
|
+
* Note: this subpath is opt-in. Direct `@moltzap/client` consumers
|
|
12
|
+
* (server-core, runtimes, test-utils) see no API-surface change.
|
|
13
|
+
*/
|
|
14
|
+
export { LeaseAlreadyConsumed, projectLeaseInvalid, catchLeaseInvalid, } from "./lease.js";
|
|
15
|
+
export { LeaseStore } from "./lease-store.js";
|
|
16
|
+
export { LeaseGuard } from "./lease-guard.js";
|
|
17
|
+
export { formatCrossConv, } from "./format-cross-conv.js";
|
|
18
|
+
export { formatGroupBlock, getGroupFields, } from "./format-group-block.js";
|
|
19
|
+
// Re-exports for ergonomics on the subpath (these are part of the public
|
|
20
|
+
// `@moltzap/client` barrel already; channel-base callers shouldn't need to
|
|
21
|
+
// import from two paths to use the formatters).
|
|
22
|
+
export { sanitizeForSystemReminder, } from "../service.js";
|
|
23
|
+
export {} from "../channel-core.js";
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/channel-base/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,GAElB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EACL,eAAe,GAGhB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,gBAAgB,EAChB,cAAc,GAGf,MAAM,yBAAyB,CAAC;AAEjC,yEAAyE;AACzE,2EAA2E;AAC3E,gDAAgD;AAChD,OAAO,EACL,yBAAyB,GAE1B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAiC,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base `LeaseGuard` — per-dispatch single-shot client-side dup-reply
|
|
3
|
+
* detection.
|
|
4
|
+
*
|
|
5
|
+
* Replaces openclaw's `consumedLeaseAt: number | null` closure in
|
|
6
|
+
* `packages/openclaw-channel/src/openclaw-entry.ts → createLeaseConsumingDeliver`.
|
|
7
|
+
* One `LeaseGuard` instance per inbound message (created inside the deliver
|
|
8
|
+
* wrapper); `consume()` returns true exactly once, false on every subsequent
|
|
9
|
+
* call.
|
|
10
|
+
*
|
|
11
|
+
* Backed by a private `number | null` field that records
|
|
12
|
+
* `Clock.currentTimeMillis` on the first consume.
|
|
13
|
+
*/
|
|
14
|
+
import { Effect, Option } from "effect";
|
|
15
|
+
export declare class LeaseGuard {
|
|
16
|
+
#private;
|
|
17
|
+
/**
|
|
18
|
+
* Returns `true` on first call (transitions internal state from
|
|
19
|
+
* "not-consumed" to "consumed-at-now"); returns `false` on every later
|
|
20
|
+
* call. Reads `Clock.currentTimeMillis` inside the Effect on first call.
|
|
21
|
+
*/
|
|
22
|
+
consume(): Effect.Effect<boolean, never, never>;
|
|
23
|
+
/**
|
|
24
|
+
* `Option.none` before the first `consume`; `Option.some(epochMs)` after,
|
|
25
|
+
* where `epochMs` is the value Clock returned at the first-consume moment.
|
|
26
|
+
* Idempotent on second-and-later reads.
|
|
27
|
+
*/
|
|
28
|
+
get consumedAt(): Effect.Effect<Option.Option<number>, never, never>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=lease-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease-guard.d.ts","sourceRoot":"","sources":["../../src/channel-base/lease-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAS,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE/C,qBAAa,UAAU;;IAGrB;;;;OAIG;IACH,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC;IAS/C;;;;OAIG;IACH,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAInE;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base `LeaseGuard` — per-dispatch single-shot client-side dup-reply
|
|
3
|
+
* detection.
|
|
4
|
+
*
|
|
5
|
+
* Replaces openclaw's `consumedLeaseAt: number | null` closure in
|
|
6
|
+
* `packages/openclaw-channel/src/openclaw-entry.ts → createLeaseConsumingDeliver`.
|
|
7
|
+
* One `LeaseGuard` instance per inbound message (created inside the deliver
|
|
8
|
+
* wrapper); `consume()` returns true exactly once, false on every subsequent
|
|
9
|
+
* call.
|
|
10
|
+
*
|
|
11
|
+
* Backed by a private `number | null` field that records
|
|
12
|
+
* `Clock.currentTimeMillis` on the first consume.
|
|
13
|
+
*/
|
|
14
|
+
import { Clock, Effect, Option } from "effect";
|
|
15
|
+
export class LeaseGuard {
|
|
16
|
+
#consumedAt = null;
|
|
17
|
+
/**
|
|
18
|
+
* Returns `true` on first call (transitions internal state from
|
|
19
|
+
* "not-consumed" to "consumed-at-now"); returns `false` on every later
|
|
20
|
+
* call. Reads `Clock.currentTimeMillis` inside the Effect on first call.
|
|
21
|
+
*/
|
|
22
|
+
consume() {
|
|
23
|
+
return Effect.gen(this, function* () {
|
|
24
|
+
if (this.#consumedAt !== null)
|
|
25
|
+
return false;
|
|
26
|
+
const ts = yield* Clock.currentTimeMillis;
|
|
27
|
+
this.#consumedAt = ts;
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `Option.none` before the first `consume`; `Option.some(epochMs)` after,
|
|
33
|
+
* where `epochMs` is the value Clock returned at the first-consume moment.
|
|
34
|
+
* Idempotent on second-and-later reads.
|
|
35
|
+
*/
|
|
36
|
+
get consumedAt() {
|
|
37
|
+
return Effect.sync(() => this.#consumedAt === null ? Option.none() : Option.some(this.#consumedAt));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=lease-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease-guard.js","sourceRoot":"","sources":["../../src/channel-base/lease-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE/C,MAAM,OAAO,UAAU;IACrB,WAAW,GAAkB,IAAI,CAAC;IAElC;;;;OAIG;IACH,OAAO;QACL,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC/B,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;YAC1C,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,IAAI,UAAU;QACZ,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CACtB,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAC1E,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base `LeaseStore` — generic per-key payload tracker.
|
|
3
|
+
*
|
|
4
|
+
* Nanoclaw uses a string-keyed, string-valued store (keyed by JID, payload
|
|
5
|
+
* is the dispatchLeaseId string). The deliberate "keep stale entry to trigger
|
|
6
|
+
* server CONSUMED rejection on retry" semantic is preserved via `peek`
|
|
7
|
+
* (read-only) — see arch sub-issue #605 §3.3 and §6.3.
|
|
8
|
+
*
|
|
9
|
+
* The class wraps a private `Map`; Effect wrappers are for caller-side
|
|
10
|
+
* composition. Single-threaded by construction — one instance lives per
|
|
11
|
+
* channel runtime, accessed from the host event loop.
|
|
12
|
+
*/
|
|
13
|
+
import { Effect, Option } from "effect";
|
|
14
|
+
export declare class LeaseStore<HostKey, T> {
|
|
15
|
+
#private;
|
|
16
|
+
/** Overwrite-on-newer-inbound. Last-write-wins per spec table. */
|
|
17
|
+
remember(key: HostKey, payload: T): Effect.Effect<void, never, never>;
|
|
18
|
+
/**
|
|
19
|
+
* Read-only lookup. Does NOT delete. Nanoclaw uses this for the
|
|
20
|
+
* stale-entry-on-retry semantic at `MoltZapChannel.sendMessage`.
|
|
21
|
+
*/
|
|
22
|
+
peek(key: HostKey): Effect.Effect<Option.Option<T>, never, never>;
|
|
23
|
+
/** Read-and-delete. `Option.none` if absent (no throw). */
|
|
24
|
+
consume(key: HostKey): Effect.Effect<Option.Option<T>, never, never>;
|
|
25
|
+
/** Clears one key (when provided) or the whole store. */
|
|
26
|
+
clear(key?: HostKey): Effect.Effect<void, never, never>;
|
|
27
|
+
/** Entry count. */
|
|
28
|
+
get size(): Effect.Effect<number, never, never>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=lease-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease-store.d.ts","sourceRoot":"","sources":["../../src/channel-base/lease-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,qBAAa,UAAU,CAAC,OAAO,EAAE,CAAC;;IAGhC,kEAAkE;IAClE,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;IAMrE;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC;IAOjE,2DAA2D;IAC3D,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC;IASpE,yDAAyD;IACzD,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;IAUvD,mBAAmB;IACnB,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAE9C;CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base `LeaseStore` — generic per-key payload tracker.
|
|
3
|
+
*
|
|
4
|
+
* Nanoclaw uses a string-keyed, string-valued store (keyed by JID, payload
|
|
5
|
+
* is the dispatchLeaseId string). The deliberate "keep stale entry to trigger
|
|
6
|
+
* server CONSUMED rejection on retry" semantic is preserved via `peek`
|
|
7
|
+
* (read-only) — see arch sub-issue #605 §3.3 and §6.3.
|
|
8
|
+
*
|
|
9
|
+
* The class wraps a private `Map`; Effect wrappers are for caller-side
|
|
10
|
+
* composition. Single-threaded by construction — one instance lives per
|
|
11
|
+
* channel runtime, accessed from the host event loop.
|
|
12
|
+
*/
|
|
13
|
+
import { Effect, Option } from "effect";
|
|
14
|
+
export class LeaseStore {
|
|
15
|
+
#entries = new Map();
|
|
16
|
+
/** Overwrite-on-newer-inbound. Last-write-wins per spec table. */
|
|
17
|
+
remember(key, payload) {
|
|
18
|
+
return Effect.sync(() => {
|
|
19
|
+
this.#entries.set(key, payload);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read-only lookup. Does NOT delete. Nanoclaw uses this for the
|
|
24
|
+
* stale-entry-on-retry semantic at `MoltZapChannel.sendMessage`.
|
|
25
|
+
*/
|
|
26
|
+
peek(key) {
|
|
27
|
+
return Effect.sync(() => {
|
|
28
|
+
const existing = this.#entries.get(key);
|
|
29
|
+
return existing === undefined ? Option.none() : Option.some(existing);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** Read-and-delete. `Option.none` if absent (no throw). */
|
|
33
|
+
consume(key) {
|
|
34
|
+
return Effect.sync(() => {
|
|
35
|
+
const existing = this.#entries.get(key);
|
|
36
|
+
if (existing === undefined)
|
|
37
|
+
return Option.none();
|
|
38
|
+
this.#entries.delete(key);
|
|
39
|
+
return Option.some(existing);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Clears one key (when provided) or the whole store. */
|
|
43
|
+
clear(key) {
|
|
44
|
+
return Effect.sync(() => {
|
|
45
|
+
if (key === undefined) {
|
|
46
|
+
this.#entries.clear();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.#entries.delete(key);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Entry count. */
|
|
54
|
+
get size() {
|
|
55
|
+
return Effect.sync(() => this.#entries.size);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=lease-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease-store.js","sourceRoot":"","sources":["../../src/channel-base/lease-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,OAAO,UAAU;IACZ,QAAQ,GAAG,IAAI,GAAG,EAAc,CAAC;IAE1C,kEAAkE;IAClE,QAAQ,CAAC,GAAY,EAAE,OAAU;QAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,GAAY;QACf,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxC,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,2DAA2D;IAC3D,OAAO,CAAC,GAAY;QAClB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;YACjD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,GAAa;QACjB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACtB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,mBAAmB;IACnB,IAAI,IAAI;QACN,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;CACF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base lease primitives.
|
|
3
|
+
*
|
|
4
|
+
* Public surface for spec C (#597):
|
|
5
|
+
* - `LeaseAlreadyConsumed`: canonical tagged error class. One definition site
|
|
6
|
+
* across all three channels (claude-code, openclaw, nanoclaw).
|
|
7
|
+
* - `projectLeaseInvalid`: predicate that narrows a `RpcServerError` to
|
|
8
|
+
* `LeaseAlreadyConsumed` when the server's wire-error payload matches the
|
|
9
|
+
* single-use-lease shape.
|
|
10
|
+
* - `catchLeaseInvalid`: Effect-pipe convenience that runs the projection
|
|
11
|
+
* inside `Effect.catchTag("RpcServerError", ...)` with
|
|
12
|
+
* `Clock.currentTimeMillis` threaded into the projector as `consumedAt`.
|
|
13
|
+
*/
|
|
14
|
+
import { Effect } from "effect";
|
|
15
|
+
import { RpcServerError } from "@moltzap/protocol";
|
|
16
|
+
declare const LeaseAlreadyConsumed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
17
|
+
readonly _tag: "LeaseAlreadyConsumed";
|
|
18
|
+
} & Readonly<A>;
|
|
19
|
+
/**
|
|
20
|
+
* The dispatch lease was already consumed (server returned the typed
|
|
21
|
+
* single-use-lease failure on a second `messages/send` for the same lease).
|
|
22
|
+
*
|
|
23
|
+
* Construction is **only** via `projectLeaseInvalid` (or `catchLeaseInvalid`).
|
|
24
|
+
* The original `RpcServerError` is preserved on `cause` so hosts can inspect
|
|
25
|
+
* the wire payload without re-fetching.
|
|
26
|
+
*
|
|
27
|
+
* See arch sub-issue #605 §3.1 for the shape rationale (cause field is the
|
|
28
|
+
* verbatim wire error; consumedAt is `Clock.currentTimeMillis` at projection
|
|
29
|
+
* time; message is derived from cause.message).
|
|
30
|
+
*/
|
|
31
|
+
export declare class LeaseAlreadyConsumed extends LeaseAlreadyConsumed_base<{
|
|
32
|
+
readonly leaseId: string;
|
|
33
|
+
readonly consumedAt: number;
|
|
34
|
+
readonly cause: RpcServerError;
|
|
35
|
+
readonly message: string;
|
|
36
|
+
}> {
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Named alias for the error channel produced by `catchLeaseInvalid` over an
|
|
40
|
+
* effect with residual error `E`. Equivalent to
|
|
41
|
+
* `LeaseAlreadyConsumed | RpcServerError | E`, but referencable from external
|
|
42
|
+
* consumers (per arch sub-issue #605 §5.1 named-error-union commitment).
|
|
43
|
+
*/
|
|
44
|
+
export type LeaseInvalidProjectionError<E> = LeaseAlreadyConsumed | RpcServerError | E;
|
|
45
|
+
/**
|
|
46
|
+
* Project an `RpcServerError` to `LeaseAlreadyConsumed` if it matches the
|
|
47
|
+
* lease-invalid wire shape; otherwise return the original error unchanged.
|
|
48
|
+
*
|
|
49
|
+
* Predicate (architect-corrected per arch sub-issue #605 §3.2):
|
|
50
|
+
* `err.data.reason === "LeaseInvalid"` OR
|
|
51
|
+
* `err.data._tag === "LeaseAlreadyConsumed"` (forward-compat for a future
|
|
52
|
+
* server that emits the canonical tag in data).
|
|
53
|
+
*
|
|
54
|
+
* The wire code (-32001 / generic Forbidden) is intentionally NOT part of
|
|
55
|
+
* the predicate because the code is too generic to discriminate on alone.
|
|
56
|
+
*
|
|
57
|
+
* `ctx.leaseId` (optional) is the lease the caller just sent. Caller-supplied
|
|
58
|
+
* because the server's `ForbiddenError.data` shape does NOT carry leaseId.
|
|
59
|
+
* Falls back to `"(unknown)"` when omitted (matches the pre-refactor
|
|
60
|
+
* claude-code behavior).
|
|
61
|
+
*
|
|
62
|
+
* `ctx.consumedAt` (required) is the epoch ms to stamp on the resulting
|
|
63
|
+
* `LeaseAlreadyConsumed.consumedAt`. Required because `LeaseAlreadyConsumed`
|
|
64
|
+
* requires it and this function is synchronous (no Clock access). Callers
|
|
65
|
+
* either pass `Date.now()` directly or use `catchLeaseInvalid` which reads
|
|
66
|
+
* `Clock.currentTimeMillis` inside the Effect.
|
|
67
|
+
*/
|
|
68
|
+
export declare function projectLeaseInvalid(err: RpcServerError, ctx: {
|
|
69
|
+
readonly leaseId?: string;
|
|
70
|
+
readonly consumedAt: number;
|
|
71
|
+
}): LeaseAlreadyConsumed | RpcServerError;
|
|
72
|
+
/**
|
|
73
|
+
* Effect-pipe convenience: catches `RpcServerError` and runs
|
|
74
|
+
* `projectLeaseInvalid` on each instance. Reads `Clock.currentTimeMillis`
|
|
75
|
+
* inside the catch and passes the result to `projectLeaseInvalid` as
|
|
76
|
+
* `consumedAt`. Matching errors are surfaced as the typed
|
|
77
|
+
* `LeaseAlreadyConsumed` on the failure channel; non-matching errors are
|
|
78
|
+
* re-raised unchanged so downstream `mapError`s see the original
|
|
79
|
+
* `RpcServerError`.
|
|
80
|
+
*
|
|
81
|
+
* Use at every channel's outbound `core.sendReply(...)` boundary:
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* core.sendReply(conv, text, { dispatchLeaseId: leaseId }).pipe(
|
|
85
|
+
* catchLeaseInvalid({ leaseId }),
|
|
86
|
+
* // ... per-channel surfacing
|
|
87
|
+
* )
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function catchLeaseInvalid<A, E2, R>(ctx?: {
|
|
91
|
+
readonly leaseId?: string;
|
|
92
|
+
}): (eff: Effect.Effect<A, RpcServerError | E2, R>) => Effect.Effect<A, LeaseInvalidProjectionError<E2>, R>;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=lease.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease.d.ts","sourceRoot":"","sources":["../../src/channel-base/lease.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAe,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;;;;AAEnD;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,SAAQ,0BAExC;IACA,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,CAAC,CAAC,IACrC,oBAAoB,GACpB,cAAc,GACd,CAAC,CAAC;AAcN;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9D,oBAAoB,GAAG,cAAc,CAQvC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,CACF,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,cAAc,GAAG,EAAE,EAAE,CAAC,CAAC,KAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,2BAA2B,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAyBxD"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-base lease primitives.
|
|
3
|
+
*
|
|
4
|
+
* Public surface for spec C (#597):
|
|
5
|
+
* - `LeaseAlreadyConsumed`: canonical tagged error class. One definition site
|
|
6
|
+
* across all three channels (claude-code, openclaw, nanoclaw).
|
|
7
|
+
* - `projectLeaseInvalid`: predicate that narrows a `RpcServerError` to
|
|
8
|
+
* `LeaseAlreadyConsumed` when the server's wire-error payload matches the
|
|
9
|
+
* single-use-lease shape.
|
|
10
|
+
* - `catchLeaseInvalid`: Effect-pipe convenience that runs the projection
|
|
11
|
+
* inside `Effect.catchTag("RpcServerError", ...)` with
|
|
12
|
+
* `Clock.currentTimeMillis` threaded into the projector as `consumedAt`.
|
|
13
|
+
*/
|
|
14
|
+
import { Clock, Data, Effect } from "effect";
|
|
15
|
+
import { RpcServerError } from "@moltzap/protocol";
|
|
16
|
+
/**
|
|
17
|
+
* The dispatch lease was already consumed (server returned the typed
|
|
18
|
+
* single-use-lease failure on a second `messages/send` for the same lease).
|
|
19
|
+
*
|
|
20
|
+
* Construction is **only** via `projectLeaseInvalid` (or `catchLeaseInvalid`).
|
|
21
|
+
* The original `RpcServerError` is preserved on `cause` so hosts can inspect
|
|
22
|
+
* the wire payload without re-fetching.
|
|
23
|
+
*
|
|
24
|
+
* See arch sub-issue #605 §3.1 for the shape rationale (cause field is the
|
|
25
|
+
* verbatim wire error; consumedAt is `Clock.currentTimeMillis` at projection
|
|
26
|
+
* time; message is derived from cause.message).
|
|
27
|
+
*/
|
|
28
|
+
export class LeaseAlreadyConsumed extends Data.TaggedError("LeaseAlreadyConsumed") {
|
|
29
|
+
}
|
|
30
|
+
const LEASE_ID_FALLBACK = "(unknown)";
|
|
31
|
+
function isLeaseInvalidData(data) {
|
|
32
|
+
if (typeof data !== "object" || data === null)
|
|
33
|
+
return false;
|
|
34
|
+
const reason = data.reason;
|
|
35
|
+
const tag = data._tag;
|
|
36
|
+
// Today's wire shape: `ForbiddenError.data.reason === "LeaseInvalid"`
|
|
37
|
+
// (arch sub-issue #605 §3.2). The `_tag` arm is forward-compat for a
|
|
38
|
+
// future server that emits the canonical tag in `data` directly.
|
|
39
|
+
return reason === "LeaseInvalid" || tag === "LeaseAlreadyConsumed";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Project an `RpcServerError` to `LeaseAlreadyConsumed` if it matches the
|
|
43
|
+
* lease-invalid wire shape; otherwise return the original error unchanged.
|
|
44
|
+
*
|
|
45
|
+
* Predicate (architect-corrected per arch sub-issue #605 §3.2):
|
|
46
|
+
* `err.data.reason === "LeaseInvalid"` OR
|
|
47
|
+
* `err.data._tag === "LeaseAlreadyConsumed"` (forward-compat for a future
|
|
48
|
+
* server that emits the canonical tag in data).
|
|
49
|
+
*
|
|
50
|
+
* The wire code (-32001 / generic Forbidden) is intentionally NOT part of
|
|
51
|
+
* the predicate because the code is too generic to discriminate on alone.
|
|
52
|
+
*
|
|
53
|
+
* `ctx.leaseId` (optional) is the lease the caller just sent. Caller-supplied
|
|
54
|
+
* because the server's `ForbiddenError.data` shape does NOT carry leaseId.
|
|
55
|
+
* Falls back to `"(unknown)"` when omitted (matches the pre-refactor
|
|
56
|
+
* claude-code behavior).
|
|
57
|
+
*
|
|
58
|
+
* `ctx.consumedAt` (required) is the epoch ms to stamp on the resulting
|
|
59
|
+
* `LeaseAlreadyConsumed.consumedAt`. Required because `LeaseAlreadyConsumed`
|
|
60
|
+
* requires it and this function is synchronous (no Clock access). Callers
|
|
61
|
+
* either pass `Date.now()` directly or use `catchLeaseInvalid` which reads
|
|
62
|
+
* `Clock.currentTimeMillis` inside the Effect.
|
|
63
|
+
*/
|
|
64
|
+
export function projectLeaseInvalid(err, ctx) {
|
|
65
|
+
if (!isLeaseInvalidData(err.data))
|
|
66
|
+
return err;
|
|
67
|
+
return new LeaseAlreadyConsumed({
|
|
68
|
+
leaseId: ctx.leaseId ?? LEASE_ID_FALLBACK,
|
|
69
|
+
consumedAt: ctx.consumedAt,
|
|
70
|
+
cause: err,
|
|
71
|
+
message: err.message,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Effect-pipe convenience: catches `RpcServerError` and runs
|
|
76
|
+
* `projectLeaseInvalid` on each instance. Reads `Clock.currentTimeMillis`
|
|
77
|
+
* inside the catch and passes the result to `projectLeaseInvalid` as
|
|
78
|
+
* `consumedAt`. Matching errors are surfaced as the typed
|
|
79
|
+
* `LeaseAlreadyConsumed` on the failure channel; non-matching errors are
|
|
80
|
+
* re-raised unchanged so downstream `mapError`s see the original
|
|
81
|
+
* `RpcServerError`.
|
|
82
|
+
*
|
|
83
|
+
* Use at every channel's outbound `core.sendReply(...)` boundary:
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* core.sendReply(conv, text, { dispatchLeaseId: leaseId }).pipe(
|
|
87
|
+
* catchLeaseInvalid({ leaseId }),
|
|
88
|
+
* // ... per-channel surfacing
|
|
89
|
+
* )
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function catchLeaseInvalid(ctx) {
|
|
93
|
+
// `Effect.catchAll` over the union (rather than `catchTag("RpcServerError",
|
|
94
|
+
// ...)`) keeps the typechecker happy when `E2` is unconstrained — without
|
|
95
|
+
// narrowing on the runtime instance, TS conservatively infers that `E2`
|
|
96
|
+
// could itself carry `_tag: "RpcServerError"`. The `instanceof` branch
|
|
97
|
+
// partitions cleanly.
|
|
98
|
+
return (eff) => Effect.catchAll(eff, (err) => {
|
|
99
|
+
if (err instanceof RpcServerError) {
|
|
100
|
+
return Effect.flatMap(Clock.currentTimeMillis, (consumedAt) => Effect.fail(projectLeaseInvalid(err, {
|
|
101
|
+
...(ctx?.leaseId !== undefined ? { leaseId: ctx.leaseId } : {}),
|
|
102
|
+
consumedAt,
|
|
103
|
+
})));
|
|
104
|
+
}
|
|
105
|
+
// Non-RpcServerError residual (E2). Re-raise unchanged so downstream
|
|
106
|
+
// `mapError`s see it without coupling to channel-base.
|
|
107
|
+
return Effect.fail(err);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=lease.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease.js","sourceRoot":"","sources":["../../src/channel-base/lease.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CACxD,sBAAsB,CAMtB;CAAG;AAaL,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAEtC,SAAS,kBAAkB,CAAC,IAAa;IACvC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,MAAM,GAAI,IAAsC,CAAC,MAAM,CAAC;IAC9D,MAAM,GAAG,GAAI,IAAoC,CAAC,IAAI,CAAC;IACvD,sEAAsE;IACtE,qEAAqE;IACrE,iEAAiE;IACjE,OAAO,MAAM,KAAK,cAAc,IAAI,GAAG,KAAK,sBAAsB,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAmB,EACnB,GAA+D;IAE/D,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC9C,OAAO,IAAI,oBAAoB,CAAC;QAC9B,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,iBAAiB;QACzC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,KAAK,EAAE,GAAG;QACV,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAW,GAE3C;IAGC,4EAA4E;IAC5E,0EAA0E;IAC1E,wEAAwE;IACxE,uEAAuE;IACvE,sBAAsB;IACtB,OAAO,CAAC,GAAG,EAAE,EAAE,CACb,MAAM,CAAC,QAAQ,CACb,GAAG,EACH,CAAC,GAAG,EAAwD,EAAE;QAC5D,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YAClC,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,UAAU,EAAE,EAAE,CAC5D,MAAM,CAAC,IAAI,CACT,mBAAmB,CAAC,GAAG,EAAE;gBACvB,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/D,UAAU;aACX,CAAC,CACH,CACF,CAAC;QACJ,CAAC;QACD,qEAAqE;QACrE,uDAAuD;QACvD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAS,CAAC,CAAC;IAChC,CAAC,CACF,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-core-context.test.d.ts","sourceRoot":"","sources":["../src/channel-core-context.test.ts"],"names":[],"mappings":""}
|