@auersperg/flare 0.3.13 → 0.3.15

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/cli/flare.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // packages/flare/package.json
4
4
  var package_default = {
5
5
  name: "@auersperg/flare",
6
- version: "0.3.13",
6
+ version: "0.3.15",
7
7
  license: "MIT",
8
8
  type: "module",
9
9
  description: "Flare client, server declarations and development CLI",
@@ -309,7 +309,7 @@ async function collectCatalog(database, now = Date.now()) {
309
309
  // packages/core/package.json
310
310
  var package_default = {
311
311
  name: "@flare/platform",
312
- version: "0.3.13",
312
+ version: "0.3.15",
313
313
  type: "module",
314
314
  license: "MIT",
315
315
  private: true,
@@ -182,8 +182,11 @@ var app_default = defineApp({ schema: {}, functions: {} });
182
182
 
183
183
  // packages/flare/dist/sdk/codex.js
184
184
  function codexInput(op, input) {
185
- boundedJson(input, 20000);
186
- if (!input || Array.isArray(input) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(input.id) || !["start", "status", "cancel"].includes(op) || Object.keys(input).some((k) => !["id", ...op === "start" ? ["prompt"] : []].includes(k)) || op === "start" && (typeof input.prompt !== "string" || !input.prompt.trim() || new TextEncoder().encode(input.prompt).length > 16384))
185
+ boundedJson(input, 40000);
186
+ if (!input || Array.isArray(input) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(input.id) || !["start", "status", "cancel", "discardArtifact"].includes(op) || Object.keys(input).some((k) => ![
187
+ "id",
188
+ ...op === "start" ? ["prompt", "instructions", "outputFile"] : []
189
+ ].includes(k)) || op === "start" && (typeof input.prompt !== "string" || !input.prompt.trim() || new TextEncoder().encode(input.prompt).length > 16384 || input.instructions !== undefined && (typeof input.instructions !== "string" || new TextEncoder().encode(input.instructions).length > 16384) || input.outputFile !== undefined && (typeof input.outputFile !== "string" || !/^[a-zA-Z0-9_-]{1,64}\.json$/.test(input.outputFile))))
187
190
  throw new FlareError("INVALID_ARGUMENT", "Invalid Codex task");
188
191
  }
189
192
  function codexContext(call) {
@@ -194,13 +197,35 @@ function codexContext(call) {
194
197
  return {
195
198
  start: (input) => invoke("start", input),
196
199
  status: (id) => invoke("status", { id }),
197
- cancel: (id) => invoke("cancel", { id })
200
+ cancel: (id) => invoke("cancel", { id }),
201
+ discardArtifact: async (id) => {
202
+ await invoke("discardArtifact", { id });
203
+ }
198
204
  };
199
205
  }
200
206
 
201
207
  // packages/flare/dist/sdk/search.js
202
208
  function searchContext(command) {
203
209
  return {
210
+ records: {
211
+ sync: (input) => command("record-sync", input),
212
+ status: (input) => command("record-status", input),
213
+ async query(input, authorize) {
214
+ if (typeof authorize !== "function")
215
+ throw new FlareError("FORBIDDEN", "A current source access/version check is required");
216
+ const hits = await command("record-query", input);
217
+ if (!hits.length)
218
+ return [];
219
+ const allowed = await authorize(hits.map(({ id, version }) => ({ id, version })));
220
+ if (!Array.isArray(allowed) || allowed.some((a) => !hits.some((h) => h.id === a.id && h.version === a.version)))
221
+ throw new FlareError("FORBIDDEN", "Invalid source access result");
222
+ const current = await command("record-validate", {
223
+ partition: input.partition,
224
+ records: allowed
225
+ });
226
+ return hits.filter((h) => current.some((a) => a.id === h.id && a.version === h.version));
227
+ }
228
+ },
204
229
  index: (fileId) => command("index", { fileId }),
205
230
  exclude: (fileId) => command("exclude", { fileId }),
206
231
  status: (fileId) => command("status", { fileId }),
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Flare application contract",
5
- "version": "5032f63300c081a5d65203bacac1e41d2fae41658f7774d2e3cd579b295734eb"
5
+ "version": "46e95c497319ce515538fb3788f93111df98c76d7ca3eafbc6fd8bcbb4cfbff1"
6
6
  },
7
7
  "components": {
8
8
  "schemas": {},
@@ -3,9 +3,9 @@
3
3
  "compatibilityDate": "2026-10-10",
4
4
  "checksums": {
5
5
  "facet": "8d7880b99b69d141a58da4c7574b26c39dbc529785512298683239dee657a38b",
6
- "action": "2b6c759576b5e75b76eee27697009ee4371f5b5f6d003d82489e21a6440e548b",
6
+ "action": "551610f76cc588409c0d5f51f23b132a7da1d90f851d49fea5186f6c8e5f7383",
7
7
  "schema": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
8
8
  "manifest": "006a07907dfd7619fa66152627bc761e581cf8ac538f5002f3b89b71ce92d733"
9
9
  },
10
- "hash": "5032f63300c081a5d65203bacac1e41d2fae41658f7774d2e3cd579b295734eb"
10
+ "hash": "46e95c497319ce515538fb3788f93111df98c76d7ca3eafbc6fd8bcbb4cfbff1"
11
11
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "format": 1,
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "stateFormat": 1,
5
5
  "protocol": 1,
6
6
  "runtimeAbi": 4,
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "path": "admin.js",
15
15
  "bytes": 37491,
16
- "sha256": "c8f7aaab485b4674e57dc1a24a059e4bd08be2fa2cbf9933b6a032425af969fe"
16
+ "sha256": "4fe2f7a229b217bfc26c0ec5708f6ae1b06306dcacdc1068f642abc901202d5a"
17
17
  },
18
18
  {
19
19
  "path": "dashboard/_astro/ai-view.D6ouaMiL.js",
@@ -122,8 +122,8 @@
122
122
  },
123
123
  {
124
124
  "path": "dashboard/bootstrap/action.js",
125
- "bytes": 12421,
126
- "sha256": "2b6c759576b5e75b76eee27697009ee4371f5b5f6d003d82489e21a6440e548b"
125
+ "bytes": 13801,
126
+ "sha256": "551610f76cc588409c0d5f51f23b132a7da1d90f851d49fea5186f6c8e5f7383"
127
127
  },
128
128
  {
129
129
  "path": "dashboard/bootstrap/api.ts",
@@ -143,12 +143,12 @@
143
143
  {
144
144
  "path": "dashboard/bootstrap/openapi.json",
145
145
  "bytes": 362,
146
- "sha256": "db5daeb8a3a83f19cb0eb978028e5860cc290d270c769974d6adc82cd41994cc"
146
+ "sha256": "d9672f489689ae8053324a9599477a438495609b1034f52b4599f1a154f4f157"
147
147
  },
148
148
  {
149
149
  "path": "dashboard/bootstrap/release.json",
150
150
  "bytes": 484,
151
- "sha256": "3cae0f2f12eb38d6d431a09890a1944f497de4b79deba9ebe9937a39fc1ac548"
151
+ "sha256": "46bd000ed0f4b7fe3b3e71d3555849103a773bf8b434af5e52e3060e5d5e4630"
152
152
  },
153
153
  {
154
154
  "path": "dashboard/bootstrap/schema.json",
@@ -192,9 +192,9 @@
192
192
  },
193
193
  {
194
194
  "path": "runtime.js",
195
- "bytes": 1220978,
196
- "sha256": "db4296fa4e80fdd00ebb9d10a058e29b799c73dd5c85e422c33b27e44439f0bd"
195
+ "bytes": 1229444,
196
+ "sha256": "3ffb8eb5dafee479a222d97cb661e49f6f4f919b2f8403ec3d2f689cb5d15082"
197
197
  }
198
198
  ],
199
- "digest": "6f78f2d407a7d7ce377924b9d4dfbcde8a63e5178e031458167a8c29f96e1360"
199
+ "digest": "53eb87d966d0698a5dab31c3bb1d3279e170df76d77ddcb67e5d8e5190c7b374"
200
200
  }
@@ -23057,8 +23057,11 @@ async function outbound(request, policy) {
23057
23057
 
23058
23058
  // packages/flare/src/sdk/codex.ts
23059
23059
  function codexInput(op, input) {
23060
- boundedJson(input, 20000);
23061
- if (!input || Array.isArray(input) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(input.id) || !["start", "status", "cancel"].includes(op) || Object.keys(input).some((k) => !["id", ...op === "start" ? ["prompt"] : []].includes(k)) || op === "start" && (typeof input.prompt !== "string" || !input.prompt.trim() || new TextEncoder().encode(input.prompt).length > 16384))
23060
+ boundedJson(input, 40000);
23061
+ if (!input || Array.isArray(input) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(input.id) || !["start", "status", "cancel", "discardArtifact"].includes(op) || Object.keys(input).some((k) => ![
23062
+ "id",
23063
+ ...op === "start" ? ["prompt", "instructions", "outputFile"] : []
23064
+ ].includes(k)) || op === "start" && (typeof input.prompt !== "string" || !input.prompt.trim() || new TextEncoder().encode(input.prompt).length > 16384 || input.instructions !== undefined && (typeof input.instructions !== "string" || new TextEncoder().encode(input.instructions).length > 16384) || input.outputFile !== undefined && (typeof input.outputFile !== "string" || !/^[a-zA-Z0-9_-]{1,64}\.json$/.test(input.outputFile))))
23062
23065
  throw new FlareError("INVALID_ARGUMENT", "Invalid Codex task");
23063
23066
  }
23064
23067
 
@@ -23626,12 +23629,38 @@ class SearchStore {
23626
23629
  }
23627
23630
  static initialize(storage) {
23628
23631
  storage.sql.exec("CREATE TABLE IF NOT EXISTS search_items (id TEXT PRIMARY KEY,key TEXT NOT NULL,version TEXT NOT NULL,binding TEXT NOT NULL,item TEXT,status TEXT NOT NULL,wanted INTEGER NOT NULL,attempts INTEGER NOT NULL DEFAULT 0,uncertain INTEGER NOT NULL DEFAULT 0,error TEXT)");
23632
+ const columns = new Set(storage.sql.exec("PRAGMA table_info(search_items)").toArray().map((r) => r.name));
23633
+ for (const name of ["record_id", "partition", "text", "metadata"])
23634
+ if (!columns.has(name))
23635
+ storage.sql.exec(`ALTER TABLE search_items ADD COLUMN ${name} TEXT`);
23636
+ storage.sql.exec("CREATE TABLE IF NOT EXISTS search_records (id TEXT NOT NULL,partition TEXT NOT NULL,version INTEGER NOT NULL,digest TEXT NOT NULL,wanted INTEGER NOT NULL,current TEXT,PRIMARY KEY(id,partition))");
23637
+ storage.sql.exec("CREATE INDEX IF NOT EXISTS search_record_versions ON search_items(record_id,partition)");
23638
+ }
23639
+ head(id, partition) {
23640
+ this.recordRef(id, partition);
23641
+ return this.storage.sql.exec("SELECT * FROM search_records WHERE id=? AND partition=?", id, partition).toArray()[0];
23642
+ }
23643
+ recordRef(id, partition) {
23644
+ fileKey(this.scope, id);
23645
+ if (typeof partition !== "string" || !partition || new TextEncoder().encode(partition).length > 128)
23646
+ fail("INVALID_INPUT");
23647
+ }
23648
+ async hash(value) {
23649
+ return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)))).map((b) => b.toString(16).padStart(2, "0")).join("");
23650
+ }
23651
+ async folder(partition) {
23652
+ return (await this.hash(JSON.stringify([this.scope, partition]))).slice(0, 32) + "/";
23629
23653
  }
23630
23654
  row(id) {
23631
23655
  fileKey(this.scope, id);
23632
23656
  return this.storage.sql.exec("SELECT * FROM search_items WHERE id=?", id).toArray()[0];
23633
23657
  }
23634
23658
  original(id) {
23659
+ const row = this.row(id);
23660
+ if (row?.record_id) {
23661
+ const head = this.head(row.record_id, row.partition);
23662
+ return head?.wanted && head.current === id ? { name: row.record_id, type: "text/plain" } : undefined;
23663
+ }
23635
23664
  return this.storage.sql.exec("SELECT name,type FROM files WHERE id=? AND state='ready'", id).toArray()[0];
23636
23665
  }
23637
23666
  binding(name = appPolicy(this.env, this.scope).search) {
@@ -23652,7 +23681,7 @@ class SearchStore {
23652
23681
  };
23653
23682
  }
23654
23683
  async reconcile() {
23655
- this.storage.sql.exec("UPDATE search_items SET wanted=0,attempts=0,error=NULL WHERE binding!=? AND wanted=1", appPolicy(this.env, this.scope).search ?? "");
23684
+ this.storage.sql.exec("UPDATE search_items SET wanted=0,attempts=0,error=NULL,text=NULL,metadata=NULL WHERE binding!=? AND wanted=1", appPolicy(this.env, this.scope).search ?? "");
23656
23685
  this.storage.sql.exec("UPDATE search_items SET status='error',error=COALESCE(error,'SEARCH_RECONCILE_REQUIRED') WHERE (wanted=0 OR status IN ('pending','sending')) AND attempts>=3");
23657
23686
  if (this.storage.sql.exec("SELECT 1 FROM search_items WHERE (wanted=0 OR status IN ('pending','sending')) AND attempts<3 LIMIT 1").toArray().length)
23658
23687
  await this.wake();
@@ -23697,7 +23726,8 @@ class SearchStore {
23697
23726
  return this.state(id);
23698
23727
  }
23699
23728
  async clear() {
23700
- this.storage.sql.exec("UPDATE search_items SET wanted=0,attempts=0,error=NULL");
23729
+ this.storage.sql.exec("UPDATE search_records SET wanted=0");
23730
+ this.storage.sql.exec("UPDATE search_items SET wanted=0,attempts=0,error=NULL,text=NULL,metadata=NULL");
23701
23731
  await this.wake();
23702
23732
  return this.summary();
23703
23733
  }
@@ -23718,6 +23748,8 @@ class SearchStore {
23718
23748
  updateInfo(row, info) {
23719
23749
  if (info.key !== row.key || !states.has(info.status))
23720
23750
  fail("SEARCH_UNAVAILABLE");
23751
+ if (row.record_id && ["queued", "running", "completed"].includes(info.status))
23752
+ this.storage.sql.exec("UPDATE search_items SET text=NULL WHERE id=?", row.id);
23721
23753
  this.storage.sql.exec("UPDATE search_items SET item=?,status=?,error=?,uncertain=0 WHERE id=? AND key=? AND wanted=1", info.id, info.status, info.status === "error" ? "INDEXING_FAILED" : null, row.id, row.key);
23722
23754
  this.storage.sql.exec("UPDATE search_items SET item=?,uncertain=0 WHERE id=? AND key=? AND wanted=0", info.id, row.id, row.key);
23723
23755
  }
@@ -23791,6 +23823,113 @@ class SearchStore {
23791
23823
  boundedJson(hits, LIMITS.outputBytes);
23792
23824
  return hits;
23793
23825
  }
23826
+ async syncRecord(input) {
23827
+ boundedJson(input, 20000);
23828
+ if (!input || Object.keys(input).some((k) => !["id", "partition", "version", "text", "metadata"].includes(k)))
23829
+ fail("INVALID_INPUT");
23830
+ this.recordRef(input.id, input.partition);
23831
+ if (!Number.isSafeInteger(input.version) || input.version < 1 || input.text !== null && (typeof input.text !== "string" || !input.text.trim() || new TextEncoder().encode(input.text).length > 16384))
23832
+ fail("INVALID_INPUT");
23833
+ const metadata = input.metadata ?? {};
23834
+ if (!metadata || Array.isArray(metadata) || typeof metadata !== "object" || Object.keys(metadata).length > 5 || Object.entries(metadata).some(([k, v]) => !/^[a-z][a-z0-9_]{0,31}$/.test(k) || ["folder", "filename", "timestamp"].includes(k) || typeof v !== "string" || new TextEncoder().encode(v).length > 64))
23835
+ fail("INVALID_INPUT");
23836
+ const digest = await this.hash(stableJson({ text: input.text, metadata }));
23837
+ const folder = await this.folder(input.partition);
23838
+ const head = this.head(input.id, input.partition);
23839
+ const binding = appPolicy(this.env, this.scope).search;
23840
+ if (head && input.version < head.version)
23841
+ return this.recordState(input);
23842
+ if (head && input.version === head.version && (digest !== head.digest || Boolean(head.wanted) !== (input.text !== null)))
23843
+ fail("CONFLICT");
23844
+ const current = head?.current ? this.row(head.current) : undefined;
23845
+ if (input.text !== null)
23846
+ this.binding(binding);
23847
+ if (head?.version === input.version && (input.text === null || current?.wanted && current.binding === binding)) {
23848
+ if (current && ["error", "skipped", "outdated"].includes(current.status))
23849
+ this.storage.sql.exec("UPDATE search_items SET status='pending',attempts=0,error=NULL,text=? WHERE id=?", input.text, current.id);
23850
+ await this.wake();
23851
+ return this.recordState(input);
23852
+ }
23853
+ this.storage.sql.exec("UPDATE search_items SET wanted=0,text=NULL,metadata=NULL,attempts=0,error=NULL WHERE record_id=? AND partition=?", input.id, input.partition);
23854
+ const id = input.text === null ? null : crypto.randomUUID();
23855
+ if (id)
23856
+ this.storage.sql.exec("INSERT INTO search_items (id,key,version,binding,status,wanted,record_id,partition,text,metadata) VALUES (?,?,?,?,'pending',1,?,?,?,?)", id, folder + id + ".txt", String(input.version), binding, input.id, input.partition, input.text, Object.keys(metadata).length ? JSON.stringify(metadata) : null);
23857
+ this.storage.sql.exec("INSERT OR REPLACE INTO search_records VALUES (?,?,?,?,?,?)", input.id, input.partition, input.version, digest, Number(input.text !== null), id);
23858
+ await this.wake();
23859
+ return this.recordState(input);
23860
+ }
23861
+ recordState(input) {
23862
+ const head = this.head(input.id, input.partition);
23863
+ const row = head?.current ? this.row(head.current) : undefined;
23864
+ const pending = this.storage.sql.exec("SELECT error FROM search_items WHERE record_id=? AND partition=? AND wanted=0", input.id, input.partition).toArray();
23865
+ return {
23866
+ id: input.id,
23867
+ version: head?.version ?? 0,
23868
+ enabled: Boolean(head?.wanted && row?.wanted && row.binding === appPolicy(this.env, this.scope).search),
23869
+ status: head?.wanted && row?.wanted ? row.status : pending.length ? "deleting" : "excluded",
23870
+ error: row?.error ?? pending.find((r) => r.error)?.error ?? null
23871
+ };
23872
+ }
23873
+ async recordStatus(input) {
23874
+ const head = this.head(input.id, input.partition);
23875
+ if (head?.current)
23876
+ await this.status(head.current);
23877
+ return this.recordState(input);
23878
+ }
23879
+ validateRecords(input) {
23880
+ if (!Array.isArray(input?.records) || input.records.length > 20)
23881
+ fail("INVALID_INPUT");
23882
+ return input.records.filter(({ id, version }) => {
23883
+ const head = this.head(id, input.partition);
23884
+ const row = head?.current ? this.row(head.current) : undefined;
23885
+ return head?.wanted && head.version === version && row?.wanted && row.binding === appPolicy(this.env, this.scope).search;
23886
+ });
23887
+ }
23888
+ async queryRecords(input) {
23889
+ boundedJson(input, 1e4);
23890
+ if (!input || typeof input.query !== "string" || !input.query.trim() || input.query.length > 2000 || Object.keys(input).some((k) => !["query", "partition", "limit"].includes(k)))
23891
+ fail("INVALID_INPUT");
23892
+ this.recordRef("00000000-0000-4000-8000-000000000000", input.partition);
23893
+ const limit = input.limit ?? 10;
23894
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 20)
23895
+ fail("INVALID_INPUT");
23896
+ const binding = appPolicy(this.env, this.scope).search;
23897
+ const instance = this.binding(binding);
23898
+ const folder = await this.folder(input.partition);
23899
+ let result;
23900
+ try {
23901
+ result = await instance.search({
23902
+ query: input.query,
23903
+ ai_search_options: {
23904
+ retrieval: {
23905
+ max_num_results: limit,
23906
+ return_on_failure: false,
23907
+ filters: { folder }
23908
+ },
23909
+ query_rewrite: { enabled: false },
23910
+ reranking: { enabled: false },
23911
+ cache: { enabled: false }
23912
+ }
23913
+ });
23914
+ } catch {
23915
+ fail("SEARCH_UNAVAILABLE");
23916
+ }
23917
+ const hits = [];
23918
+ for (const chunk of result.chunks.slice(0, limit)) {
23919
+ const row = this.storage.sql.exec("SELECT s.record_id,h.version FROM search_items s JOIN search_records h ON h.current=s.id WHERE s.key=? AND s.binding=? AND s.partition=? AND s.wanted=1 AND h.wanted=1", chunk.item.key, binding, input.partition).toArray()[0];
23920
+ if (!row)
23921
+ continue;
23922
+ if (typeof chunk.text !== "string" || !Number.isFinite(chunk.score))
23923
+ fail("SEARCH_UNAVAILABLE");
23924
+ hits.push({
23925
+ id: String(row.record_id),
23926
+ version: Number(row.version),
23927
+ text: chunk.text.slice(0, 2000),
23928
+ score: chunk.score
23929
+ });
23930
+ }
23931
+ return hits;
23932
+ }
23794
23933
  work() {
23795
23934
  return this.working ??= this.process().finally(() => {
23796
23935
  this.working = undefined;
@@ -23833,21 +23972,23 @@ class SearchStore {
23833
23972
  if (existing[0] && !["error", "skipped", "outdated"].includes(existing[0].status)) {
23834
23973
  this.updateInfo(row, existing[0]);
23835
23974
  } else {
23836
- const object = await this.env.FILES?.get(fileKey(this.scope, row.id));
23837
- if (!object)
23975
+ const object = row.record_id ? null : await this.env.FILES?.get(fileKey(this.scope, row.id));
23976
+ const content = row.record_id ? this.row(row.id)?.text : object?.body;
23977
+ if (content == null)
23838
23978
  fail("NOT_FOUND");
23839
- if (row.version && object.version !== row.version) {
23979
+ if (object && row.version && object.version !== row.version) {
23840
23980
  await object.body.cancel();
23841
23981
  fail("NOT_FOUND");
23842
23982
  }
23843
23983
  if (!this.row(row.id)?.wanted || !this.original(row.id)) {
23844
- await object.body.cancel();
23984
+ await object?.body.cancel();
23845
23985
  continue;
23846
23986
  }
23847
- this.storage.sql.exec("UPDATE search_items SET status='sending',uncertain=1,version=? WHERE id=? AND wanted=1", object.version, row.id);
23848
- this.updateInfo(row, await instance.items.uploadAndPoll(row.key, object.body, {
23987
+ this.storage.sql.exec("UPDATE search_items SET status='sending',uncertain=1,version=? WHERE id=? AND wanted=1", object?.version ?? row.version, row.id);
23988
+ this.updateInfo(row, await instance.items.uploadAndPoll(row.key, content, {
23849
23989
  timeoutMs: 30000,
23850
- pollIntervalMs: 2000
23990
+ pollIntervalMs: 2000,
23991
+ ...row.metadata ? { metadata: JSON.parse(row.metadata) } : {}
23851
23992
  }));
23852
23993
  }
23853
23994
  } catch (error) {
@@ -24208,7 +24349,7 @@ function dashboardIdentity(value) {
24208
24349
  // packages/core/package.json
24209
24350
  var package_default = {
24210
24351
  name: "@flare/platform",
24211
- version: "0.3.13",
24352
+ version: "0.3.15",
24212
24353
  type: "module",
24213
24354
  license: "MIT",
24214
24355
  private: true,
@@ -32605,6 +32746,14 @@ class EnvironmentSupervisor extends DurableObject {
32605
32746
  async searchCommand(op, input) {
32606
32747
  await this.lifecycle.start();
32607
32748
  this.assertAvailable();
32749
+ if (op === "record-sync")
32750
+ return this.search().syncRecord(input);
32751
+ if (op === "record-status")
32752
+ return this.search().recordStatus(input);
32753
+ if (op === "record-query")
32754
+ return this.search().queryRecords(input);
32755
+ if (op === "record-validate")
32756
+ return this.search().validateRecords(input);
32608
32757
  if (op === "index")
32609
32758
  return this.search().index(input.fileId);
32610
32759
  if (op === "exclude")
@@ -2,6 +2,10 @@ export interface CodexTaskInput {
2
2
  /** Stable UUID for retries of the same submission; retained for seven days. */
3
3
  id: string;
4
4
  prompt: string;
5
+ /** Trusted application instructions, separate from the user's request. */
6
+ instructions?: string;
7
+ /** Optional JSON artifact in /workspace; collected only on successful completion. */
8
+ outputFile?: string;
5
9
  }
6
10
  export interface CodexTask {
7
11
  id: string;
@@ -9,12 +13,15 @@ export interface CodexTask {
9
13
  startedAt: number;
10
14
  finishedAt?: number;
11
15
  result?: string;
16
+ artifact?: string;
12
17
  error?: string;
13
18
  }
14
19
  export interface CodexContext {
15
20
  start(input: CodexTaskInput): Promise<CodexTask>;
16
21
  status(id: string): Promise<CodexTask | null>;
17
22
  cancel(id: string): Promise<CodexTask | null>;
23
+ /** Erase the temporary artifact and prevent its later retention. An unstarted ID is fenced. */
24
+ discardArtifact(id: string): Promise<void>;
18
25
  }
19
26
  export declare function codexInput(op: string, input: any): void;
20
27
  export declare function codexContext(call: (op: string, input: unknown) => Promise<any>): CodexContext;
package/dist/sdk/codex.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import { FlareError, boundedJson } from "./protocol.js";
2
2
  export function codexInput(op, input) {
3
- boundedJson(input, 20000);
4
- if (!input || Array.isArray(input) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(input.id) || !["start", "status", "cancel"].includes(op) || Object.keys(input).some((k) => !["id", ...op === "start" ? ["prompt"] : []].includes(k)) || op === "start" && (typeof input.prompt !== "string" || !input.prompt.trim() || new TextEncoder().encode(input.prompt).length > 16384))
3
+ boundedJson(input, 40000);
4
+ if (!input || Array.isArray(input) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(input.id) || !["start", "status", "cancel", "discardArtifact"].includes(op) || Object.keys(input).some((k) => ![
5
+ "id",
6
+ ...op === "start" ? ["prompt", "instructions", "outputFile"] : []
7
+ ].includes(k)) || op === "start" && (typeof input.prompt !== "string" || !input.prompt.trim() || new TextEncoder().encode(input.prompt).length > 16384 || input.instructions !== undefined && (typeof input.instructions !== "string" || new TextEncoder().encode(input.instructions).length > 16384) || input.outputFile !== undefined && (typeof input.outputFile !== "string" || !/^[a-zA-Z0-9_-]{1,64}\.json$/.test(input.outputFile))))
5
8
  throw new FlareError("INVALID_ARGUMENT", "Invalid Codex task");
6
9
  }
7
10
  export function codexContext(call) {
@@ -12,6 +15,9 @@ export function codexContext(call) {
12
15
  return {
13
16
  start: (input) => invoke("start", input),
14
17
  status: (id) => invoke("status", { id }),
15
- cancel: (id) => invoke("cancel", { id })
18
+ cancel: (id) => invoke("cancel", { id }),
19
+ discardArtifact: async (id) => {
20
+ await invoke("discardArtifact", { id });
21
+ }
16
22
  };
17
23
  }
@@ -11,7 +11,37 @@ export type SearchHit = {
11
11
  text: string;
12
12
  score: number;
13
13
  };
14
+ export type SearchRecord = {
15
+ /** Stable source UUID and monotonically increasing source revision. */
16
+ id: string;
17
+ version: number;
18
+ /** Server-selected access partition; never accept an unchecked client owner. */
19
+ partition: string;
20
+ /** null excludes the record; retain a source tombstone for delayed jobs. */
21
+ text: string | null;
22
+ metadata?: Record<string, string>;
23
+ };
24
+ export type SearchRecordRef = Pick<SearchRecord, "id" | "version">;
25
+ export type SearchRecordState = SearchRecordRef & Omit<SearchState, "fileId">;
26
+ export type SearchRecordHit = SearchRecordRef & {
27
+ text: string;
28
+ score: number;
29
+ };
30
+ export interface SearchRecords {
31
+ /** Schedule from a source mutation's transactional outbox; re-read source in the job. */
32
+ sync(input: SearchRecord): Promise<SearchRecordState>;
33
+ status(input: {
34
+ id: string;
35
+ partition: string;
36
+ }): Promise<SearchRecordState>;
37
+ query(input: {
38
+ query: string;
39
+ partition: string;
40
+ limit?: number;
41
+ }, authorize: (records: SearchRecordRef[]) => Promise<SearchRecordRef[]>): Promise<SearchRecordHit[]>;
42
+ }
14
43
  export interface SearchContext {
44
+ readonly records: SearchRecords;
15
45
  index(fileId: string): Promise<SearchState>;
16
46
  exclude(fileId: string): Promise<SearchState>;
17
47
  status(fileId: string): Promise<SearchState>;
@@ -1,6 +1,25 @@
1
1
  import { FlareError } from "./protocol.js";
2
2
  export function searchContext(command) {
3
3
  return {
4
+ records: {
5
+ sync: (input) => command("record-sync", input),
6
+ status: (input) => command("record-status", input),
7
+ async query(input, authorize) {
8
+ if (typeof authorize !== "function")
9
+ throw new FlareError("FORBIDDEN", "A current source access/version check is required");
10
+ const hits = await command("record-query", input);
11
+ if (!hits.length)
12
+ return [];
13
+ const allowed = await authorize(hits.map(({ id, version }) => ({ id, version })));
14
+ if (!Array.isArray(allowed) || allowed.some((a) => !hits.some((h) => h.id === a.id && h.version === a.version)))
15
+ throw new FlareError("FORBIDDEN", "Invalid source access result");
16
+ const current = await command("record-validate", {
17
+ partition: input.partition,
18
+ records: allowed
19
+ });
20
+ return hits.filter((h) => current.some((a) => a.id === h.id && a.version === h.version));
21
+ }
22
+ },
4
23
  index: (fileId) => command("index", { fileId }),
5
24
  exclude: (fileId) => command("exclude", { fileId }),
6
25
  status: (fileId) => command("status", { fileId }),
@@ -2,7 +2,7 @@ export type { CodexSubscription } from "./codex-stream.js";
2
2
  import type { CodexContext } from "./codex.js";
3
3
  export type { CodexContext, CodexTask, CodexTaskInput } from "./codex.js";
4
4
  import type { SearchContext } from "./search.js";
5
- export type { SearchContext, SearchHit, SearchState } from "./search.js";
5
+ export type { SearchContext, SearchHit, SearchState, SearchRecord, SearchRecordRef, SearchRecordState, SearchRecordHit, SearchRecords, } from "./search.js";
6
6
  export { z } from "zod";
7
7
  import type { StandardSchemaV1 } from "@standard-schema/spec";
8
8
  import type { Validator, Schema } from "./schema.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auersperg/flare",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Flare client, server declarations and development CLI",