@undefineds.co/linx 0.3.20 → 0.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/generated/version.js +1 -1
  2. package/dist/index.js +6 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/auto-mode/pod-persistence.js +53 -3
  5. package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
  6. package/dist/lib/auto-mode/secretary.js +2 -2
  7. package/dist/lib/auto-mode/secretary.js.map +1 -1
  8. package/dist/lib/chat-api.js +23 -61
  9. package/dist/lib/chat-api.js.map +1 -1
  10. package/dist/lib/codex-plugin/index.js +1 -0
  11. package/dist/lib/codex-plugin/index.js.map +1 -1
  12. package/dist/lib/codex-plugin/symphony-mcp.js +335 -0
  13. package/dist/lib/codex-plugin/symphony-mcp.js.map +1 -0
  14. package/dist/lib/linx-cloud-errors.js +0 -5
  15. package/dist/lib/linx-cloud-errors.js.map +1 -1
  16. package/dist/lib/linx-status-line.js +1 -8
  17. package/dist/lib/linx-status-line.js.map +1 -1
  18. package/dist/lib/models.js +3 -2
  19. package/dist/lib/models.js.map +1 -1
  20. package/dist/lib/pi-adapter/auth.js +68 -0
  21. package/dist/lib/pi-adapter/auth.js.map +1 -0
  22. package/dist/lib/pi-adapter/branding.js +34 -103
  23. package/dist/lib/pi-adapter/branding.js.map +1 -1
  24. package/dist/lib/pi-adapter/interactive.js +35 -49
  25. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  26. package/dist/lib/pi-adapter/pod-mirror.js +38 -107
  27. package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
  28. package/dist/lib/pi-adapter/pod-native.js +2 -0
  29. package/dist/lib/pi-adapter/pod-native.js.map +1 -1
  30. package/dist/lib/pi-adapter/pod-tools.js +140 -0
  31. package/dist/lib/pi-adapter/pod-tools.js.map +1 -0
  32. package/dist/lib/pi-adapter/runtime.js +2 -12
  33. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  34. package/dist/lib/pi-adapter/session.js +13 -17
  35. package/dist/lib/pi-adapter/session.js.map +1 -1
  36. package/dist/lib/pi-adapter/stream.js +2 -20
  37. package/dist/lib/pi-adapter/stream.js.map +1 -1
  38. package/dist/lib/pod-chat-store.js +53 -4
  39. package/dist/lib/pod-chat-store.js.map +1 -1
  40. package/dist/lib/resource-identity.js +2 -0
  41. package/dist/lib/resource-identity.js.map +1 -0
  42. package/dist/lib/status-line-command.js +2 -2
  43. package/dist/lib/status-line-command.js.map +1 -1
  44. package/dist/lib/symphony/archive.js +15 -37
  45. package/dist/lib/symphony/archive.js.map +1 -1
  46. package/dist/lib/symphony/pod-projection.js +189 -1346
  47. package/dist/lib/symphony/pod-projection.js.map +1 -1
  48. package/dist/lib/symphony-command.js +209 -109
  49. package/dist/lib/symphony-command.js.map +1 -1
  50. package/dist/plugins/linx-symphony-codex/.codex-plugin/plugin.json +38 -0
  51. package/dist/plugins/linx-symphony-codex/.mcp.json +10 -0
  52. package/dist/plugins/linx-symphony-codex/README.md +9 -0
  53. package/dist/plugins/linx-symphony-codex/hooks.json +60 -0
  54. package/dist/plugins/linx-symphony-codex/scripts/symphony-hook-events.mjs +119 -0
  55. package/dist/plugins/linx-symphony-codex/scripts/symphony-mcp.mjs +335 -0
  56. package/dist/plugins/linx-symphony-codex/skills/symphony/SKILL.md +791 -0
  57. package/dist/skills/symphony/SKILL.md +7 -0
  58. package/dist/skills/xpod-cli/SKILL.md +2 -13
  59. package/package.json +4 -4
  60. package/vendor/agent-runtime/dist/chat-reconciler.d.ts +33 -0
  61. package/vendor/agent-runtime/dist/chat-reconciler.js +108 -0
  62. package/vendor/agent-runtime/dist/index.d.ts +4 -1
  63. package/vendor/agent-runtime/dist/index.js +4 -1
  64. package/vendor/agent-runtime/dist/matrix-client.d.ts +149 -0
  65. package/vendor/agent-runtime/dist/matrix-client.js +220 -0
  66. package/vendor/agent-runtime/dist/pod-resource-identity.d.ts +17 -0
  67. package/vendor/agent-runtime/dist/pod-resource-identity.js +54 -0
  68. package/vendor/agent-runtime/dist/reconciler.d.ts +0 -11
  69. package/vendor/agent-runtime/dist/reconciler.js +5 -43
  70. package/vendor/agent-runtime/dist/symphony.d.ts +272 -27
  71. package/vendor/agent-runtime/dist/symphony.js +1268 -21
  72. package/vendor/agent-runtime/dist/workspace.d.ts +61 -0
  73. package/vendor/agent-runtime/dist/workspace.js +81 -0
  74. package/vendor/agent-runtime/package.json +5 -1
  75. package/vendor/stores/dist/current-pod-base.d.ts +2 -0
  76. package/vendor/stores/dist/current-pod-base.js +14 -0
  77. package/vendor/stores/dist/exact-records.d.ts +7 -0
  78. package/vendor/stores/dist/exact-records.js +87 -0
  79. package/vendor/stores/dist/index.d.ts +1 -0
  80. package/vendor/stores/dist/index.js +1 -0
  81. package/vendor/stores/dist/login.d.ts +51 -0
  82. package/vendor/stores/dist/login.js +195 -0
  83. package/vendor/stores/dist/pod-collection.d.ts +28 -0
  84. package/vendor/stores/dist/pod-collection.js +194 -0
  85. package/vendor/stores/dist/pod-write-guard.d.ts +5 -0
  86. package/vendor/stores/dist/pod-write-guard.js +133 -0
  87. package/vendor/stores/dist/symphony-control.d.ts +245 -0
  88. package/vendor/stores/dist/symphony-control.js +2175 -0
  89. package/vendor/stores/package.json +14 -0
  90. package/dist/lib/capture/persistence.js +0 -377
  91. package/dist/lib/capture/persistence.js.map +0 -1
  92. package/dist/lib/capture/tool.js +0 -242
  93. package/dist/lib/capture/tool.js.map +0 -1
  94. package/dist/skills/basic/SKILL.md +0 -46
  95. package/dist/skills/capture/SKILL.md +0 -165
  96. package/vendor/agent-runtime/dist/coordination.d.ts +0 -93
  97. package/vendor/agent-runtime/dist/coordination.js +0 -145
@@ -581,6 +581,13 @@ Worker-facing minimum contract:
581
581
  should move to models/drizzle-solid/xpod, live-verification gaps, or
582
582
  deferred cleanup. Workers may recommend follow-up, but they do not decide
583
583
  whether it becomes a new Issue.
584
+ - If the LinX Symphony Codex plugin is installed, prefer its `linx-symphony`
585
+ MCP tools to check and write the delivery. The MCP helper only validates/writes
586
+ the portable Delivery file; it does not directly mutate Pod Issues, Tasks,
587
+ Deliveries, or acceptance state.
588
+ - Otherwise include the same terminal facts in the final report using the
589
+ `symphonyFinal: true` JSON envelope so LinX can archive the transcript through
590
+ shared control-plane use-cases; it is not a separate worker schema.
584
591
  - Never use chat, Delivery, or repo docs to redefine scope, acceptance,
585
592
  compatibility, lifecycle state, or release boundary. Write an Implementation
586
593
  Change Request instead.
@@ -18,14 +18,6 @@ secrets.
18
18
  - Read or write RDF resources when the user asks for Pod-level state.
19
19
  - Manage AI/provider credentials through secret-safe flows.
20
20
  - Verify that LinX, xpod, or another Solid app wrote the expected resource.
21
- - In Symphony mode, inspect and mutate control-plane resources through `xpod`
22
- when the AI is acting from the terminal/tool surface. This includes Idea,
23
- Issue, Task, Delivery, Run, RunStep, Report, Evidence, ApprovalRequest,
24
- InputRequest, and InboxNotification resources.
25
- - In capture workflows, use `xpod obj` for model-backed `CaptureCandidate` and
26
- `CaptureEvent` resources after the `capture` skill has decided what should
27
- be written. xpod does not decide whether something is worth capturing or how
28
- it should be classified.
29
21
 
30
22
  Do not use this skill for xpod CLI repository maintenance. If the task is to
31
23
  change xpod command behavior or release xpod itself, use the xpod project docs
@@ -57,12 +49,9 @@ Summarize human-facing output instead of pasting large raw payloads.
57
49
  - Authentication has one Solid-app source: `$SOLID_HOME/auth/credentials.json`
58
50
  (default `~/.solid/auth/credentials.json`). Old `~/.xpod/config.json` /
59
51
  `~/.xpod/secrets.json` files are app-local stale files, not Solid auth; if
60
- only those exist, report unauthenticated. If `xpod auth status --json` reports
61
- a different WebID or Pod root than the shared Solid auth file, treat that as an
62
- auth-store mismatch and stop before writing.
52
+ only those exist, report unauthenticated.
63
53
  - For product resources, do not guess paths. Use model-backed `xpod obj`
64
- commands or inspect existing links first. Do not hand-patch modeled product
65
- TTL just because a path appears obvious.
54
+ commands or inspect existing links first.
66
55
 
67
56
  ## Useful Patterns
68
57
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@undefineds.co/linx",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "linx": "dist/index.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "tsc -p tsconfig.json --noEmit && node ./scripts/build.mjs",
10
+ "build": "node ./scripts/build.mjs",
11
11
  "dev": "node ./scripts/dev.mjs",
12
12
  "test": "yarn build && NODE_OPTIONS=\"${NODE_OPTIONS:+$NODE_OPTIONS }--preserve-symlinks\" node --test --test-concurrency=1 test/**/*.test.mjs",
13
13
  "test:live-acp": "node ./scripts/smoke-auto-mode-acp.mjs"
@@ -18,8 +18,8 @@
18
18
  "@earendil-works/pi-ai": "0.78.0",
19
19
  "@earendil-works/pi-coding-agent": "0.78.0",
20
20
  "@earendil-works/pi-tui": "0.78.0",
21
- "@undefineds.co/drizzle-solid": "^0.3.14",
22
- "@undefineds.co/models": "0.2.44",
21
+ "@undefineds.co/drizzle-solid": "0.3.16",
22
+ "@undefineds.co/models": "0.2.42",
23
23
  "@zed-industries/codex-acp": "0.14.0",
24
24
  "n3": "^1.26.0",
25
25
  "pi-web-access": "^0.10.7",
@@ -0,0 +1,33 @@
1
+ import { type ReconcileDecisionSummary, type ReconcilerActorRef, type ReconcilerClientContext, type ThreadControlEvent, type ThreadPolicy, type ThreadPolicyKind } from './reconciler.js';
2
+ export type ChatAppendRole = 'user' | 'assistant' | 'system';
3
+ export type ChatAppendSource = 'web-chat' | 'cli-chat-store' | 'cli-pi-mirror' | 'cli-auto-mode' | 'service-runtime' | 'matrix' | 'secretary-runtime-intent' | 'primary-agent' | 'runtime' | 'worker' | (string & {});
4
+ export interface ChatAppendReconcilerInput {
5
+ chat?: string;
6
+ thread: string;
7
+ resource?: string;
8
+ role: ChatAppendRole;
9
+ content: string;
10
+ actor?: ReconcilerActorRef;
11
+ source?: ChatAppendSource;
12
+ policy?: ThreadPolicyKind | ThreadPolicy;
13
+ autoEnabled?: boolean;
14
+ secretaryAgent?: string;
15
+ defaultAssistantAgent?: string;
16
+ createdAt?: Date | string | number;
17
+ randomId?: string;
18
+ client?: ReconcilerClientContext;
19
+ data?: Record<string, unknown>;
20
+ }
21
+ export interface ChatAppendReconcilerResult {
22
+ event: ThreadControlEvent;
23
+ summary: ReconcileDecisionSummary;
24
+ }
25
+ export interface ChatReconcilerMetadata {
26
+ version: 1;
27
+ latest: ReconcileDecisionSummary;
28
+ decisions: ReconcileDecisionSummary[];
29
+ }
30
+ export declare function reconcileChatAppend(input: ChatAppendReconcilerInput): ChatAppendReconcilerResult;
31
+ export declare function createChatAppendEvent(input: ChatAppendReconcilerInput): ThreadControlEvent;
32
+ export declare function appendChatReconcilerMetadata(metadata: Record<string, unknown> | null | undefined, summary: ReconcileDecisionSummary): Record<string, unknown>;
33
+ export declare function readChatReconcilerMetadata(value: unknown): ChatReconcilerMetadata | null;
@@ -0,0 +1,108 @@
1
+ import { decideThreadControlEvent } from './thread-reconciler-controller.js';
2
+ export function reconcileChatAppend(input) {
3
+ const event = createChatAppendEvent(input);
4
+ const { summary } = decideThreadControlEvent({
5
+ policy: resolveChatAppendPolicy(input),
6
+ event,
7
+ ...(input.chat ? { chat: input.chat } : {}),
8
+ thread: input.thread,
9
+ ...(input.createdAt !== undefined ? { now: toDate(input.createdAt) } : {}),
10
+ randomId: input.randomId ?? input.resource,
11
+ ...(input.client ? { client: input.client } : {}),
12
+ });
13
+ return { event, summary };
14
+ }
15
+ export function createChatAppendEvent(input) {
16
+ const actor = input.actor ?? defaultActorForChatAppend(input);
17
+ return {
18
+ type: 'message.appended',
19
+ ...(input.chat ? { chat: input.chat } : {}),
20
+ thread: input.thread,
21
+ ...(input.resource ? { resource: input.resource } : {}),
22
+ actor,
23
+ content: input.content,
24
+ ...(input.createdAt !== undefined ? { createdAt: toDate(input.createdAt).toISOString() } : {}),
25
+ data: {
26
+ ...(input.data ?? {}),
27
+ role: input.role,
28
+ ...(input.source ? { source: input.source } : {}),
29
+ },
30
+ };
31
+ }
32
+ export function appendChatReconcilerMetadata(metadata, summary) {
33
+ const base = isRecord(metadata) ? { ...metadata } : {};
34
+ const existing = readChatReconcilerMetadata(base.reconciler);
35
+ base.reconciler = {
36
+ version: 1,
37
+ latest: summary,
38
+ decisions: [...(existing?.decisions ?? []), summary],
39
+ };
40
+ return base;
41
+ }
42
+ export function readChatReconcilerMetadata(value) {
43
+ if (!isRecord(value)) {
44
+ return null;
45
+ }
46
+ const latest = isRecord(value.latest) ? value.latest : undefined;
47
+ const decisions = Array.isArray(value.decisions)
48
+ ? value.decisions.filter(isRecord).map((item) => item)
49
+ : [];
50
+ if (!latest && decisions.length === 0) {
51
+ return null;
52
+ }
53
+ return {
54
+ version: 1,
55
+ latest: latest ?? decisions[decisions.length - 1],
56
+ decisions: decisions.length > 0 ? decisions : [latest],
57
+ };
58
+ }
59
+ function resolveChatAppendPolicy(input) {
60
+ if (input.policy) {
61
+ return input.policy;
62
+ }
63
+ if (input.autoEnabled) {
64
+ return {
65
+ kind: 'auto',
66
+ secretaryAgent: input.secretaryAgent ?? '__secretary__',
67
+ defaultAssistantAgent: input.defaultAssistantAgent,
68
+ };
69
+ }
70
+ return {
71
+ kind: 'direct',
72
+ defaultAssistantAgent: input.defaultAssistantAgent ?? 'primary-agent',
73
+ };
74
+ }
75
+ function defaultActorForChatAppend(input) {
76
+ if (input.role === 'user') {
77
+ return { role: 'user' };
78
+ }
79
+ if (input.source === 'secretary-runtime-intent') {
80
+ return { id: input.secretaryAgent ?? '__secretary__', role: 'secretary' };
81
+ }
82
+ if (input.source === 'worker') {
83
+ return { role: 'worker' };
84
+ }
85
+ if (input.source === 'runtime') {
86
+ return { role: 'runtime' };
87
+ }
88
+ if (input.source === 'primary-agent') {
89
+ return { id: input.defaultAssistantAgent ?? 'primary-agent', role: 'primary-agent' };
90
+ }
91
+ if (input.role === 'assistant') {
92
+ return { role: 'assistant' };
93
+ }
94
+ return { role: 'runtime' };
95
+ }
96
+ function toDate(value) {
97
+ if (value instanceof Date) {
98
+ return value;
99
+ }
100
+ const date = new Date(value);
101
+ if (Number.isNaN(date.getTime())) {
102
+ throw new Error(`Invalid chat append timestamp: ${String(value)}`);
103
+ }
104
+ return date;
105
+ }
106
+ function isRecord(value) {
107
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
108
+ }
@@ -3,9 +3,11 @@ export * from './agent-runtime.js';
3
3
  export * from './auto-mode.js';
4
4
  export * from './companion-model.js';
5
5
  export * from './client-inbox-subscription.js';
6
+ export * from './chat-reconciler.js';
6
7
  export * from './control-plane.js';
7
- export * from './coordination.js';
8
8
  export * from './file-sync.js';
9
+ export * from './matrix-client.js';
10
+ export * from './pod-resource-identity.js';
9
11
  export * from './reconciler.js';
10
12
  export * from './runtime.js';
11
13
  export * from './symphony.js';
@@ -13,3 +15,4 @@ export * from './sync.js';
13
15
  export * from './thread-reconciler-controller.js';
14
16
  export * from './turn-controller.js';
15
17
  export * from './wake-scheduler.js';
18
+ export * from './workspace.js';
@@ -3,9 +3,11 @@ export * from './agent-runtime.js';
3
3
  export * from './auto-mode.js';
4
4
  export * from './companion-model.js';
5
5
  export * from './client-inbox-subscription.js';
6
+ export * from './chat-reconciler.js';
6
7
  export * from './control-plane.js';
7
- export * from './coordination.js';
8
8
  export * from './file-sync.js';
9
+ export * from './matrix-client.js';
10
+ export * from './pod-resource-identity.js';
9
11
  export * from './reconciler.js';
10
12
  export * from './runtime.js';
11
13
  export * from './symphony.js';
@@ -13,3 +15,4 @@ export * from './sync.js';
13
15
  export * from './thread-reconciler-controller.js';
14
16
  export * from './turn-controller.js';
15
17
  export * from './wake-scheduler.js';
18
+ export * from './workspace.js';
@@ -0,0 +1,149 @@
1
+ export interface MatrixVersionsResponse {
2
+ versions: string[];
3
+ unstable_features?: Record<string, boolean>;
4
+ }
5
+ export interface MatrixLoginFlowsResponse {
6
+ flows: Array<{
7
+ type: string;
8
+ }>;
9
+ }
10
+ export interface MatrixCreateRoomRequest {
11
+ visibility?: 'private' | 'public';
12
+ room_alias_name?: string;
13
+ name?: string;
14
+ topic?: string;
15
+ invite?: string[];
16
+ creation_content?: Record<string, unknown>;
17
+ initial_state?: Array<{
18
+ type: string;
19
+ state_key?: string;
20
+ content?: Record<string, unknown>;
21
+ }>;
22
+ preset?: string;
23
+ is_direct?: boolean;
24
+ }
25
+ export interface MatrixCreateRoomResponse {
26
+ room_id: string;
27
+ }
28
+ export interface MatrixWhoamiResponse {
29
+ user_id: string;
30
+ device_id?: string;
31
+ is_guest?: boolean;
32
+ }
33
+ export interface MatrixJoinedRoomsResponse {
34
+ joined_rooms: string[];
35
+ }
36
+ export interface MatrixJoinRoomResponse {
37
+ room_id: string;
38
+ }
39
+ export interface MatrixInviteRequest {
40
+ user_id: string;
41
+ }
42
+ export interface MatrixSetStateResponse {
43
+ event_id: string;
44
+ }
45
+ export interface MatrixSendEventRequest {
46
+ body?: string;
47
+ msgtype?: string;
48
+ [key: string]: unknown;
49
+ }
50
+ export interface MatrixSendEventResponse {
51
+ event_id: string;
52
+ }
53
+ export interface MatrixClientEvent {
54
+ event_id: string;
55
+ room_id: string;
56
+ type: string;
57
+ sender: string;
58
+ origin_server_ts: number;
59
+ content: Record<string, unknown>;
60
+ state_key?: string;
61
+ unsigned?: Record<string, unknown>;
62
+ }
63
+ export interface MatrixSyncResponse {
64
+ next_batch: string;
65
+ rooms: {
66
+ join: Record<string, {
67
+ state: {
68
+ events: MatrixClientEvent[];
69
+ };
70
+ timeline: {
71
+ events: MatrixClientEvent[];
72
+ limited: boolean;
73
+ prev_batch?: string;
74
+ };
75
+ }>;
76
+ };
77
+ }
78
+ export interface MatrixMessagesResponse {
79
+ chunk: MatrixClientEvent[];
80
+ start?: string;
81
+ end: string;
82
+ }
83
+ export interface MatrixMembersResponse {
84
+ chunk: MatrixClientEvent[];
85
+ }
86
+ export interface MatrixClientOptions {
87
+ baseUrl: string;
88
+ accessToken?: string;
89
+ tokenType?: 'Bearer' | 'DPoP' | (string & {});
90
+ fetch?: typeof fetch;
91
+ randomId?: () => string;
92
+ }
93
+ export interface MatrixSyncOptions {
94
+ since?: string;
95
+ limit?: number;
96
+ timeout?: number;
97
+ }
98
+ export interface MatrixMessagesOptions {
99
+ from?: string;
100
+ dir?: 'b' | 'f';
101
+ limit?: number;
102
+ }
103
+ export declare class MatrixClientError extends Error {
104
+ readonly status: number;
105
+ readonly errcode?: string;
106
+ readonly body?: unknown;
107
+ constructor(message: string, input: {
108
+ status: number;
109
+ errcode?: string;
110
+ body?: unknown;
111
+ });
112
+ }
113
+ export declare class MatrixClient {
114
+ private readonly baseUrl;
115
+ private readonly accessToken?;
116
+ private readonly tokenType;
117
+ private readonly fetchImpl;
118
+ private readonly randomId;
119
+ constructor(options: MatrixClientOptions);
120
+ versions(): Promise<MatrixVersionsResponse>;
121
+ loginFlows(): Promise<MatrixLoginFlowsResponse>;
122
+ whoami(): Promise<MatrixWhoamiResponse>;
123
+ createRoom(input?: MatrixCreateRoomRequest): Promise<MatrixCreateRoomResponse>;
124
+ joinedRooms(): Promise<MatrixJoinedRoomsResponse>;
125
+ joinRoom(roomIdOrAlias: string): Promise<MatrixJoinRoomResponse>;
126
+ joinRoomById(roomId: string): Promise<MatrixJoinRoomResponse>;
127
+ inviteUser(roomId: string, userId: string): Promise<Record<string, never>>;
128
+ leaveRoom(roomId: string): Promise<Record<string, never>>;
129
+ sendMessage(roomId: string, body: string, options?: {
130
+ msgtype?: string;
131
+ txnId?: string;
132
+ content?: Omit<MatrixSendEventRequest, 'body' | 'msgtype'>;
133
+ }): Promise<MatrixSendEventResponse>;
134
+ sendEvent(roomId: string, eventType: string, txnId: string, content?: MatrixSendEventRequest | Record<string, unknown>): Promise<MatrixSendEventResponse>;
135
+ sync(options?: MatrixSyncOptions): Promise<MatrixSyncResponse>;
136
+ messages(roomId: string, options?: MatrixMessagesOptions): Promise<MatrixMessagesResponse>;
137
+ getEvent(roomId: string, eventId: string): Promise<MatrixClientEvent>;
138
+ getState(roomId: string, eventType: string, stateKey?: string): Promise<Record<string, unknown>>;
139
+ setState(roomId: string, eventType: string, stateKey: string, content?: Record<string, unknown>): Promise<MatrixSetStateResponse>;
140
+ members(roomId: string): Promise<MatrixMembersResponse>;
141
+ private request;
142
+ }
143
+ export declare function createMatrixClient(options: MatrixClientOptions): MatrixClient;
144
+ export declare function matrixServerNameFromBaseUrl(baseUrl: string): string;
145
+ export declare function matrixUserIdFromWebId(webId: string, serverName: string): string;
146
+ export declare function matrixLocalpartFromUserId(userId: string): string;
147
+ export declare function matrixRoomSurfaceId(roomId: string): Promise<string>;
148
+ export declare function matrixChatResourceIdFromRoomId(roomId: string): Promise<string>;
149
+ export declare function matrixThreadResourceIdFromRoomId(roomId: string): Promise<string>;
@@ -0,0 +1,220 @@
1
+ export class MatrixClientError extends Error {
2
+ status;
3
+ errcode;
4
+ body;
5
+ constructor(message, input) {
6
+ super(message);
7
+ this.name = 'MatrixClientError';
8
+ this.status = input.status;
9
+ this.errcode = input.errcode;
10
+ this.body = input.body;
11
+ }
12
+ }
13
+ export class MatrixClient {
14
+ baseUrl;
15
+ accessToken;
16
+ tokenType;
17
+ fetchImpl;
18
+ randomId;
19
+ constructor(options) {
20
+ const baseUrl = options.baseUrl.trim();
21
+ if (!baseUrl) {
22
+ throw new Error('MatrixClient requires baseUrl');
23
+ }
24
+ this.baseUrl = baseUrl.replace(/\/+$/, '');
25
+ this.accessToken = options.accessToken;
26
+ this.tokenType = options.tokenType ?? 'Bearer';
27
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
28
+ if (typeof this.fetchImpl !== 'function') {
29
+ throw new Error('MatrixClient requires a fetch implementation');
30
+ }
31
+ this.randomId = options.randomId ?? defaultRandomId;
32
+ }
33
+ async versions() {
34
+ return this.request('GET', '/_matrix/client/versions');
35
+ }
36
+ async loginFlows() {
37
+ return this.request('GET', '/_matrix/client/v3/login');
38
+ }
39
+ async whoami() {
40
+ return this.request('GET', '/_matrix/client/v3/account/whoami');
41
+ }
42
+ async createRoom(input = {}) {
43
+ return this.request('POST', '/_matrix/client/v3/createRoom', input);
44
+ }
45
+ async joinedRooms() {
46
+ return this.request('GET', '/_matrix/client/v3/joined_rooms');
47
+ }
48
+ async joinRoom(roomIdOrAlias) {
49
+ return this.request('POST', `/_matrix/client/v3/join/${encodePathSegment(roomIdOrAlias)}`, {});
50
+ }
51
+ async joinRoomById(roomId) {
52
+ return this.request('POST', `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/join`, {});
53
+ }
54
+ async inviteUser(roomId, userId) {
55
+ return this.request('POST', `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/invite`, {
56
+ user_id: userId,
57
+ });
58
+ }
59
+ async leaveRoom(roomId) {
60
+ return this.request('POST', `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/leave`, {});
61
+ }
62
+ async sendMessage(roomId, body, options = {}) {
63
+ return this.sendEvent(roomId, 'm.room.message', options.txnId ?? this.randomId(), {
64
+ ...(options.content ?? {}),
65
+ msgtype: options.msgtype ?? 'm.text',
66
+ body,
67
+ });
68
+ }
69
+ async sendEvent(roomId, eventType, txnId, content = {}) {
70
+ return this.request('PUT', `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/send/${encodePathSegment(eventType)}/${encodePathSegment(txnId)}`, content);
71
+ }
72
+ async sync(options = {}) {
73
+ return this.request('GET', appendQuery('/_matrix/client/v3/sync', {
74
+ since: options.since,
75
+ limit: options.limit,
76
+ timeout: options.timeout,
77
+ }));
78
+ }
79
+ async messages(roomId, options = {}) {
80
+ return this.request('GET', appendQuery(`/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/messages`, {
81
+ from: options.from,
82
+ dir: options.dir,
83
+ limit: options.limit,
84
+ }));
85
+ }
86
+ async getEvent(roomId, eventId) {
87
+ return this.request('GET', `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/event/${encodePathSegment(eventId)}`);
88
+ }
89
+ async getState(roomId, eventType, stateKey = '') {
90
+ const statePath = stateKey
91
+ ? `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/state/${encodePathSegment(eventType)}/${encodePathSegment(stateKey)}`
92
+ : `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/state/${encodePathSegment(eventType)}`;
93
+ return this.request('GET', statePath);
94
+ }
95
+ async setState(roomId, eventType, stateKey, content = {}) {
96
+ const statePath = stateKey
97
+ ? `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/state/${encodePathSegment(eventType)}/${encodePathSegment(stateKey)}`
98
+ : `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/state/${encodePathSegment(eventType)}`;
99
+ return this.request('PUT', statePath, content);
100
+ }
101
+ async members(roomId) {
102
+ return this.request('GET', `/_matrix/client/v3/rooms/${encodePathSegment(roomId)}/members`);
103
+ }
104
+ async request(method, path, body) {
105
+ const headers = new Headers();
106
+ headers.set('Accept', 'application/json');
107
+ if (body !== undefined) {
108
+ headers.set('Content-Type', 'application/json');
109
+ }
110
+ if (this.accessToken) {
111
+ headers.set('Authorization', `${this.tokenType} ${this.accessToken}`);
112
+ }
113
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
114
+ method,
115
+ headers,
116
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
117
+ });
118
+ const parsed = await parseMatrixJson(response);
119
+ if (!response.ok) {
120
+ const errorBody = isRecord(parsed) ? parsed : {};
121
+ const message = typeof errorBody.error === 'string'
122
+ ? errorBody.error
123
+ : `Matrix request failed: ${response.status}`;
124
+ throw new MatrixClientError(message, {
125
+ status: response.status,
126
+ errcode: typeof errorBody.errcode === 'string' ? errorBody.errcode : undefined,
127
+ body: parsed,
128
+ });
129
+ }
130
+ return parsed;
131
+ }
132
+ }
133
+ export function createMatrixClient(options) {
134
+ return new MatrixClient(options);
135
+ }
136
+ export function matrixServerNameFromBaseUrl(baseUrl) {
137
+ return new URL(baseUrl).host;
138
+ }
139
+ export function matrixUserIdFromWebId(webId, serverName) {
140
+ return `@${matrixLocalpartFromUserId(webId)}:${serverName}`;
141
+ }
142
+ export function matrixLocalpartFromUserId(userId) {
143
+ try {
144
+ const url = new URL(userId);
145
+ const withoutHash = `${url.pathname}${url.hash}`.replace(/^\/+/, '');
146
+ return matrixSlug(withoutHash || url.host);
147
+ }
148
+ catch {
149
+ return matrixSlug(userId);
150
+ }
151
+ }
152
+ export async function matrixRoomSurfaceId(roomId) {
153
+ const digest = await sha256Hex(roomId);
154
+ return `matrix-${digest.slice(0, 16)}`;
155
+ }
156
+ export async function matrixChatResourceIdFromRoomId(roomId) {
157
+ return `${await matrixRoomSurfaceId(roomId)}/index.ttl#this`;
158
+ }
159
+ export async function matrixThreadResourceIdFromRoomId(roomId) {
160
+ return `chat/${await matrixRoomSurfaceId(roomId)}/index.ttl#thread`;
161
+ }
162
+ function appendQuery(path, values) {
163
+ const params = new URLSearchParams();
164
+ for (const [key, value] of Object.entries(values)) {
165
+ if (value === undefined) {
166
+ continue;
167
+ }
168
+ params.set(key, String(value));
169
+ }
170
+ const query = params.toString();
171
+ return query ? `${path}?${query}` : path;
172
+ }
173
+ function encodePathSegment(value) {
174
+ return encodeURIComponent(value);
175
+ }
176
+ async function parseMatrixJson(response) {
177
+ const text = await response.text();
178
+ if (!text.trim()) {
179
+ return null;
180
+ }
181
+ try {
182
+ return JSON.parse(text);
183
+ }
184
+ catch (error) {
185
+ if (!response.ok) {
186
+ throw new MatrixClientError(`Matrix request returned non-JSON error: ${response.status}`, {
187
+ status: response.status,
188
+ body: text,
189
+ });
190
+ }
191
+ throw error;
192
+ }
193
+ }
194
+ function defaultRandomId() {
195
+ return globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2, 12);
196
+ }
197
+ function isRecord(value) {
198
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
199
+ }
200
+ function matrixSlug(value) {
201
+ return value.toLowerCase().replace(/[^a-z0-9._=-]+/g, '_').replace(/^_+|_+$/g, '') || 'user';
202
+ }
203
+ async function sha256Hex(value) {
204
+ const subtle = await resolveSubtleCrypto();
205
+ if (!subtle) {
206
+ throw new Error('Matrix room resource mapping requires Web Crypto SHA-256 support.');
207
+ }
208
+ const digest = await subtle.digest('SHA-256', new TextEncoder().encode(value));
209
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('');
210
+ }
211
+ async function resolveSubtleCrypto() {
212
+ if (globalThis.crypto?.subtle) {
213
+ return globalThis.crypto.subtle;
214
+ }
215
+ if (typeof process !== 'undefined' && process.versions?.node) {
216
+ const nodeCrypto = await import('node:crypto');
217
+ return nodeCrypto.webcrypto.subtle;
218
+ }
219
+ return null;
220
+ }
@@ -0,0 +1,17 @@
1
+ declare const baseRelativeResourceIdBrand: unique symbol;
2
+ declare const resourceIriBrand: unique symbol;
3
+ export type BaseRelativeResourceId = string & {
4
+ readonly [baseRelativeResourceIdBrand]: 'BaseRelativeResourceId';
5
+ };
6
+ export type ResourceIri = string & {
7
+ readonly [resourceIriBrand]: 'ResourceIri';
8
+ };
9
+ export declare function asBaseRelativeResourceId(value: string, label?: string): BaseRelativeResourceId;
10
+ export declare function asResourceIri(value: string, label?: string): ResourceIri;
11
+ export declare function requireRowResourceId(row: {
12
+ id?: string | null;
13
+ } | null | undefined, label?: string): BaseRelativeResourceId;
14
+ export declare function agentResourceId(key?: string | null): BaseRelativeResourceId;
15
+ export declare function agentKeyFromResourceId(resourceId: string): string;
16
+ export declare function agentHomeDirFromResourceId(resourceId: string): BaseRelativeResourceId;
17
+ export {};