@adecore/agents 0.33.1 → 0.33.3

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.
@@ -5,6 +5,7 @@ export interface RecordDirectoryOptions<T> {
5
5
  schema: z.ZodType<T>;
6
6
  idOf(record: T): string;
7
7
  write?: (path: string, text: string) => Promise<void>;
8
+ remove?: (path: string) => Promise<void>;
8
9
  keep?(record: T): T | null;
9
10
  }
10
11
  export declare class RecordDirectory<T> {
@@ -12,9 +13,10 @@ export declare class RecordDirectory<T> {
12
13
  private readonly schema;
13
14
  private readonly idOf;
14
15
  private readonly writeFile;
16
+ private readonly removeFile;
15
17
  private readonly keep;
16
18
  private readonly records;
17
- private readonly writes;
19
+ private readonly files;
18
20
  constructor(options: RecordDirectoryOptions<T>);
19
21
  load(): Promise<void>;
20
22
  get(id: string): T | undefined;
@@ -16,15 +16,20 @@ export class RecordDirectory {
16
16
  schema;
17
17
  idOf;
18
18
  writeFile;
19
+ removeFile;
19
20
  keep;
20
21
  records = new Map();
21
- // One write at a time per record, so an older one never lands after a newer one.
22
- writes = new KeyedSerializer();
22
+ /*
23
+ * One write or remove at a time per record, so an older write never lands after a newer one and
24
+ * no delete starts while another of the same file is pending, which Windows can fail with EFAULT.
25
+ */
26
+ files = new KeyedSerializer();
23
27
  constructor(options) {
24
28
  this.dir = options.dir;
25
29
  this.schema = options.schema;
26
30
  this.idOf = options.idOf;
27
31
  this.writeFile = options.write ?? writeAtomic;
32
+ this.removeFile = options.remove ?? removeFile;
28
33
  this.keep = options.keep ?? null;
29
34
  }
30
35
  /* Reads what an earlier run of the host wrote down. Call before anything can ask. */
@@ -59,7 +64,7 @@ export class RecordDirectory {
59
64
  }
60
65
  const record = this.keep === null ? parsed.data : this.keep(parsed.data);
61
66
  if (record === null) {
62
- await removeFile(join(this.dir, name));
67
+ await this.removeFile(join(this.dir, name));
63
68
  continue;
64
69
  }
65
70
  this.records.set(this.idOf(record), record);
@@ -82,23 +87,23 @@ export class RecordDirectory {
82
87
  write(record) {
83
88
  const id = this.idOf(record);
84
89
  this.records.set(id, record);
85
- return this.writes.run(id, async () => {
90
+ return this.files.run(id, async () => {
86
91
  if (this.records.get(id) !== record) {
87
92
  return false;
88
93
  }
89
94
  await mkdir(this.dir, { recursive: true, mode: 0o700 });
90
95
  await this.writeFile(this.pathOf(id), JSON.stringify(record));
91
96
  if (!this.records.has(id)) {
92
- await removeFile(this.pathOf(id));
97
+ await this.removeFile(this.pathOf(id));
93
98
  return false;
94
99
  }
95
100
  return true;
96
101
  });
97
102
  }
98
- /* Forgets a record here and now; its file follows. */
103
+ /* Forgets a record here and now; its file follows, after what is queued for it. */
99
104
  remove(id) {
100
105
  this.records.delete(id);
101
- return removeFile(this.pathOf(id));
106
+ return this.files.run(id, () => this.removeFile(this.pathOf(id)));
102
107
  }
103
108
  async prune(drop) {
104
109
  for (const listed of this.all()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents",
3
- "version": "0.33.1",
3
+ "version": "0.33.3",
4
4
  "description": "Node-compatible agent chat hosts, providers, accounts, usage and durable task coordination.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -81,8 +81,8 @@
81
81
  "test": "bun --conditions=source test"
82
82
  },
83
83
  "dependencies": {
84
- "@adecore/agent-contracts": "0.33.1",
85
- "@adecore/intelligent-ui": "0.33.1",
84
+ "@adecore/agent-contracts": "0.33.3",
85
+ "@adecore/intelligent-ui": "0.33.3",
86
86
  "zod": "^4.6.5"
87
87
  },
88
88
  "devDependencies": {
@@ -14,6 +14,8 @@ export interface RecordDirectoryOptions<T> {
14
14
  schema: z.ZodType<T>;
15
15
  idOf(record: T): string;
16
16
  write?: (path: string, text: string) => Promise<void>;
17
+ // Stands in for `removeFile` whole, so it must take a missing file as removed: a write and a remove delete twice.
18
+ remove?: (path: string) => Promise<void>;
17
19
  /*
18
20
  * What a record read from disk is worth keeping as, for a store that drops what went stale while
19
21
  * the host was down. Null takes the file with it.
@@ -31,16 +33,21 @@ export class RecordDirectory<T> {
31
33
  private readonly schema: z.ZodType<T>;
32
34
  private readonly idOf: (record: T) => string;
33
35
  private readonly writeFile: (path: string, text: string) => Promise<void>;
36
+ private readonly removeFile: (path: string) => Promise<void>;
34
37
  private readonly keep: ((record: T) => T | null) | null;
35
38
  private readonly records = new Map<string, T>();
36
- // One write at a time per record, so an older one never lands after a newer one.
37
- private readonly writes = new KeyedSerializer();
39
+ /*
40
+ * One write or remove at a time per record, so an older write never lands after a newer one and
41
+ * no delete starts while another of the same file is pending, which Windows can fail with EFAULT.
42
+ */
43
+ private readonly files = new KeyedSerializer();
38
44
 
39
45
  constructor(options: RecordDirectoryOptions<T>) {
40
46
  this.dir = options.dir;
41
47
  this.schema = options.schema;
42
48
  this.idOf = options.idOf;
43
49
  this.writeFile = options.write ?? writeAtomic;
50
+ this.removeFile = options.remove ?? removeFile;
44
51
  this.keep = options.keep ?? null;
45
52
  }
46
53
 
@@ -74,7 +81,7 @@ export class RecordDirectory<T> {
74
81
  }
75
82
  const record = this.keep === null ? parsed.data : this.keep(parsed.data);
76
83
  if (record === null) {
77
- await removeFile(join(this.dir, name));
84
+ await this.removeFile(join(this.dir, name));
78
85
  continue;
79
86
  }
80
87
  this.records.set(this.idOf(record), record);
@@ -101,24 +108,24 @@ export class RecordDirectory<T> {
101
108
  write(record: T): Promise<boolean> {
102
109
  const id = this.idOf(record);
103
110
  this.records.set(id, record);
104
- return this.writes.run(id, async () => {
111
+ return this.files.run(id, async () => {
105
112
  if (this.records.get(id) !== record) {
106
113
  return false;
107
114
  }
108
115
  await mkdir(this.dir, { recursive: true, mode: 0o700 });
109
116
  await this.writeFile(this.pathOf(id), JSON.stringify(record));
110
117
  if (!this.records.has(id)) {
111
- await removeFile(this.pathOf(id));
118
+ await this.removeFile(this.pathOf(id));
112
119
  return false;
113
120
  }
114
121
  return true;
115
122
  });
116
123
  }
117
124
 
118
- /* Forgets a record here and now; its file follows. */
125
+ /* Forgets a record here and now; its file follows, after what is queued for it. */
119
126
  remove(id: string): Promise<void> {
120
127
  this.records.delete(id);
121
- return removeFile(this.pathOf(id));
128
+ return this.files.run(id, () => this.removeFile(this.pathOf(id)));
122
129
  }
123
130
 
124
131
  async prune(drop: (record: T) => boolean): Promise<void> {