@amalgm/shell 0.1.126 → 0.1.128

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.
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Direct-to-staging content placement for Cold Add.
3
+ *
4
+ * During Cold Add each ordinary file's bytes are written once, directly into
5
+ * their hidden staged destination: every chunk is verified before its
6
+ * positioned write, the whole-file hash is proven from that same stream, the
7
+ * complete file is fsynced, and the workspace is revealed later by one atomic
8
+ * rename. A second cache copy is not part of the critical path.
9
+ *
10
+ * SQLite chunk evidence makes an interrupted staging resumable. Retained
11
+ * staged bytes are never trusted: every retained chunk is re-read and
12
+ * re-hashed against its manifest before it may satisfy the download, and the
13
+ * whole-file digest is proven again from that same stream.
14
+ */
15
+ import type { ContentManifest, TransferArtifact } from "@amalgm/live";
16
+ import type { SealedContentFile } from "./content-cache-host.js";
17
+ /** The shape every download-set target satisfies, whether it stages bytes in
18
+ * the machine cache or directly inside a hidden Add staging root. */
19
+ export interface ContentDownloadTarget {
20
+ hasChunk(index: number): Promise<boolean>;
21
+ putChunk(index: number, bytes: Uint8Array): Promise<void>;
22
+ seal(): Promise<SealedContentFile>;
23
+ close(): Promise<void>;
24
+ }
25
+ /** Routing one download set applies when an Add stages ordinary files
26
+ * directly: which content hashes land at which staged destinations, and
27
+ * where their durable resume evidence lives. */
28
+ export interface AddStagingRouting {
29
+ readonly evidenceDir: string;
30
+ readonly destinations: ReadonlyMap<string, readonly string[]>;
31
+ }
32
+ /** The device-scoped home of one workspace Add's staging resume evidence.
33
+ * It lives beside the ground database — never inside the staging root, which
34
+ * the reveal renames into user-visible ground. */
35
+ export declare function addStagingEvidenceDir(databasePath: string, workspaceId: string): string;
36
+ export declare function clearAddStagingEvidence(databasePath: string, workspaceId: string): void;
37
+ /** Chunk-level resume evidence for one Add's staged files. Disposable: the
38
+ * staged bytes are re-proven against the manifest whenever a row is used. */
39
+ declare class AddStagingEvidence {
40
+ private readonly database;
41
+ constructor(evidenceDir: string);
42
+ chunk(contentHash: string, index: number): {
43
+ sha256: string;
44
+ bytes: number;
45
+ } | null;
46
+ record(contentHash: string, index: number, sha256: string, bytes: number): void;
47
+ forget(contentHash: string, index: number): void;
48
+ close(): void;
49
+ }
50
+ /** One ordinary file downloading directly into its staged destination.
51
+ *
52
+ * Chunks may arrive in any order; each lands with a positioned write, and a
53
+ * serialized drain feeds the whole-file hasher strictly in chunk order — from
54
+ * the arriving bytes when the next expected chunk is at hand, otherwise by
55
+ * reading an already-written later chunk when its turn arrives. This keeps no
56
+ * unaccounted buffers and cannot deadlock failure settlement. Resume rereads
57
+ * every retained chunk by necessity so local staging can never stand in for
58
+ * content proof. */
59
+ export declare class StagedContentFile implements ContentDownloadTarget {
60
+ private readonly evidence;
61
+ readonly artifact: TransferArtifact;
62
+ readonly manifest: ContentManifest;
63
+ private readonly destinations;
64
+ private readonly primary;
65
+ private readonly offsets;
66
+ private readonly hasher;
67
+ private readonly ready;
68
+ private nextChunk;
69
+ private hashTurn;
70
+ private prepared;
71
+ private sealed;
72
+ constructor(evidence: AddStagingEvidence, artifact: TransferArtifact, manifest: ContentManifest, destinations: readonly string[]);
73
+ private prepare;
74
+ /** Feed the whole-file hasher every chunk that is now in order. Turns are
75
+ * serialized: parallel chunk arrivals only mark readiness. */
76
+ private queueHash;
77
+ /** Re-prove one retained staged chunk before it may satisfy the download.
78
+ * Evidence rows only say what should be there; the bytes decide. */
79
+ hasChunk(index: number): Promise<boolean>;
80
+ /** Persist one block already verified by @amalgm/live with a positioned
81
+ * write, then advance the streaming whole-file proof. */
82
+ putChunk(index: number, bytes: Uint8Array): Promise<void>;
83
+ /** Prove the streamed whole-file digest, fsync, and place every duplicate
84
+ * destination. The staged file needs no rename of its own: the whole
85
+ * staging root is revealed by one atomic rename after rows commit. */
86
+ seal(): Promise<SealedContentFile>;
87
+ /** Keep staged bytes and evidence for the next attempt. */
88
+ close(): Promise<void>;
89
+ }
90
+ /** One evidence database shared by every staged file in a download set. */
91
+ export declare class StagedContentSet {
92
+ private readonly evidence;
93
+ private closed;
94
+ constructor(evidenceDir: string);
95
+ target(artifact: TransferArtifact, manifest: ContentManifest, destinations: readonly string[]): StagedContentFile;
96
+ close(): void;
97
+ }
98
+ export {};
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Direct-to-staging content placement for Cold Add.
3
+ *
4
+ * During Cold Add each ordinary file's bytes are written once, directly into
5
+ * their hidden staged destination: every chunk is verified before its
6
+ * positioned write, the whole-file hash is proven from that same stream, the
7
+ * complete file is fsynced, and the workspace is revealed later by one atomic
8
+ * rename. A second cache copy is not part of the critical path.
9
+ *
10
+ * SQLite chunk evidence makes an interrupted staging resumable. Retained
11
+ * staged bytes are never trusted: every retained chunk is re-read and
12
+ * re-hashed against its manifest before it may satisfy the download, and the
13
+ * whole-file digest is proven again from that same stream.
14
+ */
15
+ import { createHash } from "node:crypto";
16
+ import { chmodSync, constants, rmSync } from "node:fs";
17
+ import { open } from "node:fs/promises";
18
+ import { dirname, join } from "node:path";
19
+ import Database from "better-sqlite3";
20
+ import { atomicCopy, ensurePrivateDir, ensureUserDir } from "./filesystem.js";
21
+ const sha256Hex = (input) => createHash("sha256").update(input).digest("hex");
22
+ /** The device-scoped home of one workspace Add's staging resume evidence.
23
+ * It lives beside the ground database — never inside the staging root, which
24
+ * the reveal renames into user-visible ground. */
25
+ export function addStagingEvidenceDir(databasePath, workspaceId) {
26
+ return join(dirname(databasePath), "add-staging", workspaceId);
27
+ }
28
+ export function clearAddStagingEvidence(databasePath, workspaceId) {
29
+ rmSync(addStagingEvidenceDir(databasePath, workspaceId), { recursive: true, force: true });
30
+ }
31
+ /** Chunk-level resume evidence for one Add's staged files. Disposable: the
32
+ * staged bytes are re-proven against the manifest whenever a row is used. */
33
+ class AddStagingEvidence {
34
+ database;
35
+ constructor(evidenceDir) {
36
+ ensurePrivateDir(evidenceDir);
37
+ this.database = new Database(join(evidenceDir, "staging.sqlite"));
38
+ this.database.pragma("busy_timeout = 5000");
39
+ this.database.pragma("journal_mode = WAL");
40
+ this.database.pragma("synchronous = NORMAL");
41
+ this.database.exec(`
42
+ CREATE TABLE IF NOT EXISTS staged_blocks (
43
+ content_hash TEXT NOT NULL,
44
+ block_index INTEGER NOT NULL,
45
+ sha256 TEXT NOT NULL,
46
+ bytes INTEGER NOT NULL,
47
+ PRIMARY KEY (content_hash, block_index)
48
+ );
49
+ `);
50
+ }
51
+ chunk(contentHash, index) {
52
+ const row = this.database.prepare(`
53
+ SELECT sha256, bytes FROM staged_blocks WHERE content_hash = ? AND block_index = ?
54
+ `).get(contentHash, index);
55
+ return row ?? null;
56
+ }
57
+ record(contentHash, index, sha256, bytes) {
58
+ this.database.prepare(`
59
+ INSERT INTO staged_blocks (content_hash, block_index, sha256, bytes)
60
+ VALUES (?, ?, ?, ?)
61
+ ON CONFLICT (content_hash, block_index) DO UPDATE SET
62
+ sha256 = excluded.sha256,
63
+ bytes = excluded.bytes
64
+ `).run(contentHash, index, sha256, bytes);
65
+ }
66
+ forget(contentHash, index) {
67
+ this.database.prepare(`
68
+ DELETE FROM staged_blocks WHERE content_hash = ? AND block_index = ?
69
+ `).run(contentHash, index);
70
+ }
71
+ close() {
72
+ this.database.close();
73
+ }
74
+ }
75
+ async function readFileRange(file, offset, bytes) {
76
+ const handle = await openRegularFile(file, constants.O_RDONLY | constants.O_NOFOLLOW);
77
+ const output = Buffer.allocUnsafe(bytes);
78
+ try {
79
+ let cursor = 0;
80
+ while (cursor < bytes) {
81
+ const result = await handle.read(output, cursor, bytes - cursor, offset + cursor);
82
+ if (result.bytesRead === 0)
83
+ throw new Error("staged content range is truncated");
84
+ cursor += result.bytesRead;
85
+ }
86
+ return output;
87
+ }
88
+ finally {
89
+ await handle.close();
90
+ }
91
+ }
92
+ async function writeFileRange(file, offset, bytes) {
93
+ const handle = await openRegularFile(file, constants.O_RDWR | constants.O_NOFOLLOW);
94
+ try {
95
+ let cursor = 0;
96
+ while (cursor < bytes.byteLength) {
97
+ const result = await handle.write(bytes, cursor, bytes.byteLength - cursor, offset + cursor);
98
+ if (result.bytesWritten === 0)
99
+ throw new Error("staged content write made no progress");
100
+ cursor += result.bytesWritten;
101
+ }
102
+ }
103
+ finally {
104
+ await handle.close();
105
+ }
106
+ }
107
+ async function fsyncFile(file) {
108
+ const handle = await openRegularFile(file, constants.O_RDWR | constants.O_NOFOLLOW);
109
+ try {
110
+ await handle.sync();
111
+ }
112
+ finally {
113
+ await handle.close();
114
+ }
115
+ }
116
+ /** Open only the regular file named at this exact path. Retained staging is
117
+ * untrusted until re-proven, so neither a stale symlink nor a special file may
118
+ * redirect Add's reads, writes, truncation, or durability boundary. */
119
+ async function openRegularFile(file, flags, mode) {
120
+ const handle = await open(file, flags, mode);
121
+ try {
122
+ if (!(await handle.stat()).isFile()) {
123
+ throw new Error(`staged content is not a regular file: ${file}`);
124
+ }
125
+ return handle;
126
+ }
127
+ catch (error) {
128
+ await handle.close();
129
+ throw error;
130
+ }
131
+ }
132
+ /** One ordinary file downloading directly into its staged destination.
133
+ *
134
+ * Chunks may arrive in any order; each lands with a positioned write, and a
135
+ * serialized drain feeds the whole-file hasher strictly in chunk order — from
136
+ * the arriving bytes when the next expected chunk is at hand, otherwise by
137
+ * reading an already-written later chunk when its turn arrives. This keeps no
138
+ * unaccounted buffers and cannot deadlock failure settlement. Resume rereads
139
+ * every retained chunk by necessity so local staging can never stand in for
140
+ * content proof. */
141
+ export class StagedContentFile {
142
+ evidence;
143
+ artifact;
144
+ manifest;
145
+ destinations;
146
+ primary;
147
+ offsets;
148
+ hasher = createHash("sha256");
149
+ ready = new Set();
150
+ nextChunk = 0;
151
+ hashTurn = Promise.resolve();
152
+ prepared = null;
153
+ sealed = false;
154
+ constructor(evidence, artifact, manifest, destinations) {
155
+ this.evidence = evidence;
156
+ this.artifact = artifact;
157
+ this.manifest = manifest;
158
+ this.destinations = destinations;
159
+ if (destinations.length === 0)
160
+ throw new Error("staged content has no destination");
161
+ this.primary = destinations[0];
162
+ let offset = 0;
163
+ this.offsets = manifest.chunks.map((chunk) => {
164
+ const result = offset;
165
+ offset += chunk.bytes;
166
+ return result;
167
+ });
168
+ }
169
+ prepare() {
170
+ if (!this.prepared) {
171
+ this.prepared = (async () => {
172
+ ensureUserDir(dirname(this.primary));
173
+ let handle;
174
+ try {
175
+ handle = await openRegularFile(this.primary, constants.O_RDWR | constants.O_NOFOLLOW);
176
+ }
177
+ catch (error) {
178
+ if (error.code !== "ENOENT")
179
+ throw error;
180
+ handle = await openRegularFile(this.primary, constants.O_RDWR | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o644);
181
+ }
182
+ try {
183
+ await handle.truncate(this.manifest.bytes);
184
+ }
185
+ finally {
186
+ await handle.close();
187
+ }
188
+ })();
189
+ }
190
+ return this.prepared;
191
+ }
192
+ /** Feed the whole-file hasher every chunk that is now in order. Turns are
193
+ * serialized: parallel chunk arrivals only mark readiness. */
194
+ queueHash(arrived, arrivedBytes) {
195
+ const turn = this.hashTurn.then(async () => {
196
+ this.ready.add(arrived);
197
+ while (this.ready.has(this.nextChunk)) {
198
+ const index = this.nextChunk;
199
+ const chunk = this.manifest.chunks[index];
200
+ const offset = this.offsets[index];
201
+ if (!chunk || offset === undefined)
202
+ throw new Error(`content manifest has no chunk ${index}`);
203
+ const bytes = index === arrived
204
+ ? arrivedBytes
205
+ : await readFileRange(this.primary, offset, chunk.bytes);
206
+ this.hasher.update(bytes);
207
+ this.ready.delete(index);
208
+ this.nextChunk += 1;
209
+ }
210
+ });
211
+ // A failed turn already rejected its own caller; keep the chain usable so
212
+ // later chunks fail on their own evidence, not on a poisoned chain.
213
+ this.hashTurn = turn.catch(() => { });
214
+ return turn;
215
+ }
216
+ /** Re-prove one retained staged chunk before it may satisfy the download.
217
+ * Evidence rows only say what should be there; the bytes decide. */
218
+ async hasChunk(index) {
219
+ const expected = this.manifest.chunks[index];
220
+ const offset = this.offsets[index];
221
+ if (!expected || offset === undefined)
222
+ throw new Error(`content manifest has no chunk ${index}`);
223
+ const retained = this.evidence.chunk(this.artifact.contentHash, index);
224
+ if (retained?.sha256 === expected.sha256 && retained.bytes === expected.bytes) {
225
+ try {
226
+ const bytes = await readFileRange(this.primary, offset, expected.bytes);
227
+ if (sha256Hex(bytes) === expected.sha256) {
228
+ await this.queueHash(index, bytes);
229
+ return true;
230
+ }
231
+ }
232
+ catch {
233
+ // The staged bytes disappeared or shrank after their evidence row.
234
+ }
235
+ this.evidence.forget(this.artifact.contentHash, index);
236
+ }
237
+ return false;
238
+ }
239
+ /** Persist one block already verified by @amalgm/live with a positioned
240
+ * write, then advance the streaming whole-file proof. */
241
+ async putChunk(index, bytes) {
242
+ const expected = this.manifest.chunks[index];
243
+ const offset = this.offsets[index];
244
+ if (!expected || offset === undefined)
245
+ throw new Error(`content manifest has no chunk ${index}`);
246
+ if (bytes.byteLength !== expected.bytes)
247
+ throw new Error("content block byte count is invalid");
248
+ await this.prepare();
249
+ await writeFileRange(this.primary, offset, bytes);
250
+ this.evidence.record(this.artifact.contentHash, index, expected.sha256, expected.bytes);
251
+ await this.queueHash(index, bytes);
252
+ }
253
+ /** Prove the streamed whole-file digest, fsync, and place every duplicate
254
+ * destination. The staged file needs no rename of its own: the whole
255
+ * staging root is revealed by one atomic rename after rows commit. */
256
+ async seal() {
257
+ if (this.sealed)
258
+ throw new Error("staged artifact is already sealed");
259
+ this.sealed = true;
260
+ await this.prepare();
261
+ await this.hashTurn;
262
+ if (this.nextChunk !== this.manifest.chunks.length) {
263
+ throw new Error(`cloud content ${this.manifest.contentHash} did not stream every chunk`);
264
+ }
265
+ if (this.hasher.digest("hex") !== this.manifest.contentHash) {
266
+ throw new Error(`cloud content ${this.manifest.contentHash} is corrupt`);
267
+ }
268
+ await fsyncFile(this.primary);
269
+ chmodSync(this.primary, 0o644);
270
+ for (const duplicate of this.destinations.slice(1)) {
271
+ ensureUserDir(dirname(duplicate));
272
+ atomicCopy(this.primary, duplicate, 0o644);
273
+ await fsyncFile(duplicate);
274
+ }
275
+ return {
276
+ file: this.primary,
277
+ bytes: this.manifest.bytes,
278
+ contentHash: this.manifest.contentHash,
279
+ };
280
+ }
281
+ /** Keep staged bytes and evidence for the next attempt. */
282
+ async close() {
283
+ if (this.prepared)
284
+ await this.prepared.catch(() => { });
285
+ }
286
+ }
287
+ /** One evidence database shared by every staged file in a download set. */
288
+ export class StagedContentSet {
289
+ evidence;
290
+ closed = false;
291
+ constructor(evidenceDir) {
292
+ this.evidence = new AddStagingEvidence(evidenceDir);
293
+ }
294
+ target(artifact, manifest, destinations) {
295
+ if (this.closed)
296
+ throw new Error("staged content set is closed");
297
+ return new StagedContentFile(this.evidence, artifact, manifest, destinations);
298
+ }
299
+ close() {
300
+ if (this.closed)
301
+ return;
302
+ this.closed = true;
303
+ this.evidence.close();
304
+ }
305
+ }
306
+ //# sourceMappingURL=workspace-add-staging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-add-staging.js","sourceRoot":"","sources":["../src/workspace-add-staging.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@amalgm/shell",
3
- "version": "0.1.126",
3
+ "version": "0.1.128",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@amalgm/shell",
9
- "version": "0.1.126",
9
+ "version": "0.1.128",
10
10
  "license": "UNLICENSED",
11
11
  "dependencies": {
12
12
  "@amalgm/agents": "0.2.0",
13
13
  "@amalgm/apps": "0.1.1",
14
- "@amalgm/automations": "0.2.2",
14
+ "@amalgm/automations": "0.2.5",
15
15
  "@amalgm/browser": "0.1.0",
16
16
  "@amalgm/channels": "0.1.1",
17
- "@amalgm/chat": "0.2.4",
18
- "@amalgm/core": "0.4.4",
17
+ "@amalgm/chat": "0.2.5",
18
+ "@amalgm/core": "0.4.5",
19
19
  "@amalgm/live": "0.2.57",
20
20
  "@amalgm/tools": "0.1.4",
21
21
  "@anthropic-ai/claude-agent-sdk": "^0.3.224",
@@ -100,9 +100,9 @@
100
100
  }
101
101
  },
102
102
  "node_modules/@amalgm/automations": {
103
- "version": "0.2.2",
104
- "resolved": "https://registry.npmjs.org/@amalgm/automations/-/automations-0.2.2.tgz",
105
- "integrity": "sha512-uIPHbWXCdBwGFyULKXEfNOnNYqpLyC6MQF+ow/+JWuq6l+WJ7TfALd4WfIfbh6c17La3ZjGVoUsWuXG/LTFLrA==",
103
+ "version": "0.2.5",
104
+ "resolved": "https://registry.npmjs.org/@amalgm/automations/-/automations-0.2.5.tgz",
105
+ "integrity": "sha512-fZG89CBW5bq1FcCn8Tb3XOJOexzRXtF9ARHUpSVvyFvwGMghjE9it2joH57/mWnZYxt92aUT+3Ate1sGaf/cxA==",
106
106
  "dependencies": {
107
107
  "@amalgm/core": "0.4.4",
108
108
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -119,6 +119,14 @@
119
119
  "node": ">=20"
120
120
  }
121
121
  },
122
+ "node_modules/@amalgm/automations/node_modules/@amalgm/core": {
123
+ "version": "0.4.4",
124
+ "resolved": "https://registry.npmjs.org/@amalgm/core/-/core-0.4.4.tgz",
125
+ "integrity": "sha512-w7/edrjsyvqWh5+nM5SRNkbyY4DjnsVMgqj4c9X+g2E3n+RqG/gNKO7M6B2eH/WNq6vNPDQ8qgeaIChGhfMRBw==",
126
+ "engines": {
127
+ "node": ">=18"
128
+ }
129
+ },
122
130
  "node_modules/@amalgm/browser": {
123
131
  "version": "0.1.0",
124
132
  "resolved": "https://registry.npmjs.org/@amalgm/browser/-/browser-0.1.0.tgz",
@@ -172,10 +180,18 @@
172
180
  "node": ">=20"
173
181
  }
174
182
  },
183
+ "node_modules/@amalgm/channels/node_modules/@amalgm/core": {
184
+ "version": "0.4.4",
185
+ "resolved": "https://registry.npmjs.org/@amalgm/core/-/core-0.4.4.tgz",
186
+ "integrity": "sha512-w7/edrjsyvqWh5+nM5SRNkbyY4DjnsVMgqj4c9X+g2E3n+RqG/gNKO7M6B2eH/WNq6vNPDQ8qgeaIChGhfMRBw==",
187
+ "engines": {
188
+ "node": ">=18"
189
+ }
190
+ },
175
191
  "node_modules/@amalgm/chat": {
176
- "version": "0.2.4",
177
- "resolved": "https://registry.npmjs.org/@amalgm/chat/-/chat-0.2.4.tgz",
178
- "integrity": "sha512-cksV7UALZJc1nuk2QeTTD9LoSqKokppk4e+34nnnExzg2mGmGAGA9vjDcESGaz67pqq59YduOd7O7a8jH9ciFA==",
192
+ "version": "0.2.5",
193
+ "resolved": "https://registry.npmjs.org/@amalgm/chat/-/chat-0.2.5.tgz",
194
+ "integrity": "sha512-1srs0GzhgmVXZBD/jiQ0SlOyzlv14IsLRfXzSwhe9j/TbSIKoFKePmhrJdOZw+LsyW4srB5FIgxAKuomvMpkIQ==",
179
195
  "dependencies": {
180
196
  "@agentclientprotocol/sdk": "^1.4.0",
181
197
  "@anthropic-ai/claude-agent-sdk": "^0.3.206",
@@ -190,9 +206,9 @@
190
206
  }
191
207
  },
192
208
  "node_modules/@amalgm/core": {
193
- "version": "0.4.4",
194
- "resolved": "https://registry.npmjs.org/@amalgm/core/-/core-0.4.4.tgz",
195
- "integrity": "sha512-w7/edrjsyvqWh5+nM5SRNkbyY4DjnsVMgqj4c9X+g2E3n+RqG/gNKO7M6B2eH/WNq6vNPDQ8qgeaIChGhfMRBw==",
209
+ "version": "0.4.5",
210
+ "resolved": "https://registry.npmjs.org/@amalgm/core/-/core-0.4.5.tgz",
211
+ "integrity": "sha512-uD5TOcYnpZlcbqziNOma4kIJ2jfyE2CE4HbgJA/BmEvrOAHw9Q6XuHd+MBazOp2x5e4hhPoPtOwFVQSzrYwsdA==",
196
212
  "engines": {
197
213
  "node": ">=18"
198
214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/shell",
3
- "version": "0.1.126",
3
+ "version": "0.1.128",
4
4
  "description": "The Amalgm runtime host assembled from the published @amalgm SDK capabilities.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -25,7 +25,8 @@
25
25
  "dist",
26
26
  "npm-shrinkwrap.json",
27
27
  "PURPOSE.md",
28
- "README.md"
28
+ "README.md",
29
+ "skills"
29
30
  ],
30
31
  "publishConfig": {
31
32
  "access": "public"
@@ -46,11 +47,11 @@
46
47
  "dependencies": {
47
48
  "@amalgm/agents": "0.2.0",
48
49
  "@amalgm/apps": "0.1.1",
49
- "@amalgm/automations": "0.2.2",
50
+ "@amalgm/automations": "0.2.5",
50
51
  "@amalgm/browser": "0.1.0",
51
52
  "@amalgm/channels": "0.1.1",
52
- "@amalgm/chat": "0.2.4",
53
- "@amalgm/core": "0.4.4",
53
+ "@amalgm/chat": "0.2.5",
54
+ "@amalgm/core": "0.4.5",
54
55
  "@amalgm/live": "0.2.57",
55
56
  "@amalgm/tools": "0.1.4",
56
57
  "@anthropic-ai/claude-agent-sdk": "^0.3.224",