@absolutejs/voice 0.0.22-beta.643 → 0.0.22-beta.645

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/index.js CHANGED
@@ -4574,6 +4574,8 @@ var createVoiceSession = (options) => {
4574
4574
  user: 0
4575
4575
  };
4576
4576
  const recordingFormats = {};
4577
+ const recordingStartedAt = Date.now();
4578
+ let assistantRecordingEndsAtMs = 0;
4577
4579
  let recordingPersisted = false;
4578
4580
  const captureRecordingChunk = (channel, bytes, format) => {
4579
4581
  if (!recordingConfig || recordingPersisted) {
@@ -4589,11 +4591,33 @@ var createVoiceSession = (options) => {
4589
4591
  if (currentTotal >= recordingMaxBytes) {
4590
4592
  return;
4591
4593
  }
4592
- const remaining = recordingMaxBytes - currentTotal;
4594
+ const bytesPerSample = 2;
4595
+ const bytesPerSecond = format.sampleRateHz * format.channels * bytesPerSample;
4596
+ let chunkStartMs = currentTotal / bytesPerSecond * 1000;
4597
+ if (channel === "assistant" || currentTotal === 0) {
4598
+ const wallClockOffsetMs = Math.max(0, Date.now() - recordingStartedAt);
4599
+ chunkStartMs = channel === "assistant" ? Math.max(wallClockOffsetMs, assistantRecordingEndsAtMs) : wallClockOffsetMs;
4600
+ const targetByteOffset = Math.floor(chunkStartMs * bytesPerSecond / 1000 / bytesPerSample) * bytesPerSample;
4601
+ const silenceByteLength = Math.min(Math.max(0, targetByteOffset - currentTotal), recordingMaxBytes - currentTotal);
4602
+ if (silenceByteLength > 0) {
4603
+ recordingBuffers[channel].push(new Uint8Array(silenceByteLength));
4604
+ recordingByteTotals[channel] += silenceByteLength;
4605
+ }
4606
+ if (channel === "assistant") {
4607
+ assistantRecordingEndsAtMs = chunkStartMs + bytes.byteLength / bytesPerSecond * 1000;
4608
+ }
4609
+ }
4610
+ const updatedTotal = recordingByteTotals[channel];
4611
+ if (updatedTotal >= recordingMaxBytes) {
4612
+ recordingFormats[channel] = format;
4613
+ return;
4614
+ }
4615
+ const remaining = recordingMaxBytes - updatedTotal;
4593
4616
  const slice = bytes.byteLength <= remaining ? bytes : bytes.subarray(0, remaining);
4594
4617
  recordingBuffers[channel].push(new Uint8Array(slice));
4595
4618
  recordingByteTotals[channel] += slice.byteLength;
4596
4619
  recordingFormats[channel] = format;
4620
+ return { byteLength: slice.byteLength, startMs: chunkStartMs };
4597
4621
  };
4598
4622
  const pruneTurnAudio = () => {
4599
4623
  const replayWindowMs = sttFallback?.replayWindowMs ?? DEFAULT_FALLBACK_REPLAY_MS;
@@ -4921,7 +4945,7 @@ var createVoiceSession = (options) => {
4921
4945
  };
4922
4946
  const sendAssistantAudio = async (chunk, input) => {
4923
4947
  const normalizedChunk = chunk instanceof Uint8Array ? new Uint8Array(chunk) : chunk instanceof ArrayBuffer ? new Uint8Array(chunk.slice(0)) : new Uint8Array(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
4924
- captureRecordingChunk("assistant", normalizedChunk, input.format);
4948
+ const recordingChunk = captureRecordingChunk("assistant", normalizedChunk, input.format);
4925
4949
  kickCallSilenceWatchdog();
4926
4950
  await send({
4927
4951
  chunkBase64: encodeBase64(normalizedChunk),
@@ -4940,6 +4964,10 @@ var createVoiceSession = (options) => {
4940
4964
  if (activeTTSTurnId) {
4941
4965
  await appendTurnLatencyStage({
4942
4966
  at: input.receivedAt,
4967
+ metadata: recordingChunk ? {
4968
+ recordingByteLength: recordingChunk.byteLength,
4969
+ recordingStartMs: recordingChunk.startMs
4970
+ } : undefined,
4943
4971
  stage: "assistant_audio_received",
4944
4972
  turnId: activeTTSTurnId
4945
4973
  });
@@ -1,2 +1,17 @@
1
1
  import type { VoicePluginConfig, VoiceSessionStore } from "./core/types";
2
- export declare const manifest: import("@absolutejs/manifest").PackageManifest<VoicePluginConfig, VoiceSessionStore>;
2
+ export declare const manifest: Omit<import("@absolutejs/manifest").PackageManifest<VoicePluginConfig, VoiceSessionStore>, "tools" | "contract" | "settings"> & (({
3
+ contract: 1;
4
+ tools?: Record<string, import("@absolutejs/manifest").LegacyManifestTool<VoiceSessionStore>> | undefined;
5
+ } | {
6
+ contract: 2;
7
+ tools?: Record<string, import("@absolutejs/manifest").AuthorizedManifestTool<VoiceSessionStore>> | undefined;
8
+ }) & {
9
+ settings: import("@sinclair/typebox").TObject<{
10
+ backchannelBargeInGuard: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
11
+ greeting: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
12
+ normalizeNumbers: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
13
+ path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
14
+ preset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"default">, import("@sinclair/typebox").TLiteral<"chat">, import("@sinclair/typebox").TLiteral<"guided-intake">, import("@sinclair/typebox").TLiteral<"dictation">, import("@sinclair/typebox").TLiteral<"noisy-room">, import("@sinclair/typebox").TLiteral<"pstn-balanced">, import("@sinclair/typebox").TLiteral<"pstn-fast">, import("@sinclair/typebox").TLiteral<"reliability">]>>;
15
+ resumeGreeting: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
16
+ }>;
17
+ });
package/dist/manifest.js CHANGED
@@ -102,14 +102,19 @@ var defineImplementation = () => (implementation) => {
102
102
  const defined = implementation;
103
103
  return defined;
104
104
  };
105
- var defineManifest = () => (manifest) => {
106
- const defined = manifest;
107
- return defined;
105
+ var defineManifest = () => (manifest) => manifest;
106
+ var literal = (value) => value;
107
+ var RUNTIME_KIND = literal("runtime");
108
+ var WORKSPACE_KIND = literal("workspace");
109
+ var toolFactory = () => {
110
+ function runtime(definition) {
111
+ return { kind: RUNTIME_KIND, ...definition };
112
+ }
113
+ function workspace(definition) {
114
+ return { kind: WORKSPACE_KIND, ...definition };
115
+ }
116
+ return { runtime, workspace };
108
117
  };
109
- var toolFactory = () => ({
110
- runtime: (definition) => ({ kind: "runtime", ...definition }),
111
- workspace: (definition) => ({ kind: "workspace", ...definition })
112
- });
113
118
  function IsAsyncIterator(value) {
114
119
  return IsObject(value) && globalThis.Symbol.asyncIterator in value;
115
120
  }
@@ -847,7 +852,7 @@ function String2(options) {
847
852
  function* FromUnion(syntax) {
848
853
  const trim = syntax.trim().replace(/"|'/g, "");
849
854
  return trim === "boolean" ? yield Boolean() : trim === "number" ? yield Number2() : trim === "bigint" ? yield BigInt2() : trim === "string" ? yield String2() : yield (() => {
850
- const literals = trim.split("|").map((literal) => Literal(literal.trim()));
855
+ const literals = trim.split("|").map((literal2) => Literal(literal2.trim()));
851
856
  return literals.length === 0 ? Never() : literals.length === 1 ? literals[0] : UnionEvaluated(literals);
852
857
  })();
853
858
  }
@@ -3555,10 +3560,10 @@ function ScoreUnion(schema, references, value) {
3555
3560
  const keys = Object.getOwnPropertyNames(value);
3556
3561
  const entries = Object.entries(object.properties);
3557
3562
  return entries.reduce((acc, [key, schema2]) => {
3558
- const literal = schema2[Kind] === "Literal" && schema2.const === value[key] ? 100 : 0;
3563
+ const literal2 = schema2[Kind] === "Literal" && schema2.const === value[key] ? 100 : 0;
3559
3564
  const checks = Check(schema2, references, value[key]) ? 10 : 0;
3560
3565
  const exists = keys.includes(key) ? 1 : 0;
3561
- return acc + (literal + checks + exists);
3566
+ return acc + (literal2 + checks + exists);
3562
3567
  }, 0);
3563
3568
  } else if (schema[Kind] === "Union") {
3564
3569
  const schemas = schema.anyOf.map((schema2) => Deref(schema2, references));
@@ -5895,7 +5900,13 @@ var wiringSnippet = Type.Object({
5895
5900
  Type.Literal("server-plugin")
5896
5901
  ]))
5897
5902
  });
5898
- var clientFrameworks = ["angular", "client", "react", "svelte", "vue"];
5903
+ var clientFrameworks = [
5904
+ "angular",
5905
+ "client",
5906
+ "react",
5907
+ "svelte",
5908
+ "vue"
5909
+ ];
5899
5910
  var wiringRecipe = Type.Object({
5900
5911
  client: Type.Optional(Type.Partial(Type.Object(Object.fromEntries(clientFrameworks.map((framework) => [framework, wiringSnippet]))))),
5901
5912
  description: Type.Optional(Type.String()),
@@ -5960,8 +5971,47 @@ var toolAnnotations = Type.Object({
5960
5971
  readOnlyHint: Type.Optional(Type.Boolean()),
5961
5972
  title: Type.Optional(Type.String())
5962
5973
  });
5974
+ var toolAuthorization = Type.Object({
5975
+ approval: Type.Union([
5976
+ Type.Literal("always"),
5977
+ Type.Literal("never"),
5978
+ Type.Literal("policy")
5979
+ ]),
5980
+ audience: Type.Union([
5981
+ Type.Literal("admin"),
5982
+ Type.Literal("authenticated"),
5983
+ Type.Literal("owner"),
5984
+ Type.Literal("public")
5985
+ ]),
5986
+ compensatingTool: Type.Optional(Type.String({ pattern: TOOL_NAME_PATTERN.source })),
5987
+ destinationFields: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { minItems: 1 })),
5988
+ destinations: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { minItems: 1 })),
5989
+ effects: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
5990
+ idempotency: Type.Optional(Type.Union([
5991
+ Type.Object({
5992
+ field: Type.String({ minLength: 1 }),
5993
+ mode: Type.Literal("field")
5994
+ }),
5995
+ Type.Object({ mode: Type.Literal("host") }),
5996
+ Type.Object({ mode: Type.Literal("resource") })
5997
+ ])),
5998
+ requiredScopes: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { minItems: 1 })),
5999
+ resource: Type.Optional(Type.Object({
6000
+ idField: Type.Optional(Type.String({ minLength: 1 })),
6001
+ ownerIdField: Type.Optional(Type.String({ minLength: 1 })),
6002
+ tenantIdField: Type.Optional(Type.String({ minLength: 1 })),
6003
+ type: Type.String({ minLength: 1 })
6004
+ })),
6005
+ reversible: Type.Optional(Type.Boolean()),
6006
+ spend: Type.Optional(Type.Object({
6007
+ amountMinorField: Type.String({ minLength: 1 }),
6008
+ currencyField: Type.String({ minLength: 1 }),
6009
+ maximumAmountMinor: Type.Optional(Type.Integer({ minimum: 0 }))
6010
+ }))
6011
+ });
5963
6012
  var serializedTool = Type.Object({
5964
6013
  annotations: Type.Optional(toolAnnotations),
6014
+ authorization: Type.Optional(toolAuthorization),
5965
6015
  capabilities: Type.Optional(Type.Array(Type.Union([
5966
6016
  Type.Literal("exec"),
5967
6017
  Type.Literal("glob"),
@@ -5973,7 +6023,15 @@ var serializedTool = Type.Object({
5973
6023
  kind: Type.Union([Type.Literal("runtime"), Type.Literal("workspace")])
5974
6024
  });
5975
6025
  var manifestSchema = Type.Object({
5976
- contract: Type.Literal(1),
6026
+ contract: Type.Union([Type.Literal(1), Type.Literal(2)]),
6027
+ discovery: Type.Optional(Type.Object({
6028
+ audiences: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
6029
+ certificationUrl: Type.Optional(Type.String()),
6030
+ intents: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
6031
+ keywords: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
6032
+ protocols: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
6033
+ url: Type.Optional(Type.String())
6034
+ })),
5977
6035
  identity: Type.Object({
5978
6036
  accent: Type.Optional(Type.String({ pattern: "^#[0-9a-fA-F]{3,8}$" })),
5979
6037
  category: Type.String({ minLength: 1 }),
@@ -5983,7 +6041,9 @@ var manifestSchema = Type.Object({
5983
6041
  svg: Type.Optional(Type.String()),
5984
6042
  url: Type.Optional(Type.String())
5985
6043
  })),
5986
- name: Type.String({ pattern: "^(@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$" }),
6044
+ name: Type.String({
6045
+ pattern: "^(@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$"
6046
+ }),
5987
6047
  tagline: Type.String({ minLength: 1 })
5988
6048
  }),
5989
6049
  implements: Type.Optional(Type.Array(adapterImplementation)),
@@ -6956,7 +7016,7 @@ function String3(options) {
6956
7016
  function* FromUnion21(syntax) {
6957
7017
  const trim = syntax.trim().replace(/"|'/g, "");
6958
7018
  return trim === "boolean" ? yield Boolean2() : trim === "number" ? yield Number3() : trim === "bigint" ? yield BigInt3() : trim === "string" ? yield String3() : yield (() => {
6959
- const literals = trim.split("|").map((literal) => Literal2(literal.trim()));
7019
+ const literals = trim.split("|").map((literal2) => Literal2(literal2.trim()));
6960
7020
  return literals.length === 0 ? Never2() : literals.length === 1 ? literals[0] : UnionEvaluated2(literals);
6961
7021
  })();
6962
7022
  }
@@ -8557,7 +8617,7 @@ var tool = toolFactory();
8557
8617
  var MAX_LIST_LIMIT = 200;
8558
8618
  var MAX_TRANSCRIPT_TURNS = 20;
8559
8619
  var manifest = defineManifest()({
8560
- contract: 1,
8620
+ contract: 2,
8561
8621
  identity: {
8562
8622
  accent: "#f43f5e",
8563
8623
  category: "voice",
@@ -8760,6 +8820,15 @@ var manifest = defineManifest()({
8760
8820
  tools: {
8761
8821
  delete_session: tool.runtime({
8762
8822
  annotations: { destructiveHint: true, idempotentHint: true },
8823
+ authorization: {
8824
+ approval: "policy",
8825
+ audience: "owner",
8826
+ effects: ["delete"],
8827
+ idempotency: { mode: "resource" },
8828
+ requiredScopes: ["voice:sessions:delete"],
8829
+ resource: { idField: "id", type: "voice-session" },
8830
+ reversible: false
8831
+ },
8763
8832
  description: "Delete one call session (its transcript and state) by id. Deleting an unknown id succeeds.",
8764
8833
  input: Type2.Object({ id: Type2.String({ minLength: 1 }) }),
8765
8834
  handler: async ({ id }, store) => {
@@ -8769,6 +8838,13 @@ var manifest = defineManifest()({
8769
8838
  }),
8770
8839
  get_session: tool.runtime({
8771
8840
  annotations: { readOnlyHint: true },
8841
+ authorization: {
8842
+ approval: "never",
8843
+ audience: "owner",
8844
+ effects: ["read"],
8845
+ requiredScopes: ["voice:sessions:read"],
8846
+ resource: { idField: "id", type: "voice-session" }
8847
+ },
8772
8848
  description: "Fetch one call session by id: status, timing, and the committed conversation turns (caller text and assistant reply), most recent last.",
8773
8849
  input: Type2.Object({
8774
8850
  id: Type2.String({ minLength: 1 }),
@@ -8796,6 +8872,12 @@ var manifest = defineManifest()({
8796
8872
  }),
8797
8873
  list_sessions: tool.runtime({
8798
8874
  annotations: { readOnlyHint: true },
8875
+ authorization: {
8876
+ approval: "never",
8877
+ audience: "owner",
8878
+ effects: ["read"],
8879
+ requiredScopes: ["voice:sessions:read"]
8880
+ },
8799
8881
  description: "List call sessions with status, creation time, last activity, and turn count \u2014 most recently active first.",
8800
8882
  input: Type2.Object({
8801
8883
  limit: Type2.Optional(Type2.Integer({ maximum: MAX_LIST_LIMIT, minimum: 1 }))
@@ -1,5 +1,5 @@
1
1
  {
2
- "contract": 1,
2
+ "contract": 2,
3
3
  "identity": {
4
4
  "accent": "#f43f5e",
5
5
  "category": "voice",
@@ -325,6 +325,24 @@
325
325
  "destructiveHint": true,
326
326
  "idempotentHint": true
327
327
  },
328
+ "authorization": {
329
+ "approval": "policy",
330
+ "audience": "owner",
331
+ "effects": [
332
+ "delete"
333
+ ],
334
+ "idempotency": {
335
+ "mode": "resource"
336
+ },
337
+ "requiredScopes": [
338
+ "voice:sessions:delete"
339
+ ],
340
+ "resource": {
341
+ "idField": "id",
342
+ "type": "voice-session"
343
+ },
344
+ "reversible": false
345
+ },
328
346
  "description": "Delete one call session (its transcript and state) by id. Deleting an unknown id succeeds.",
329
347
  "input": {
330
348
  "type": "object",
@@ -344,6 +362,20 @@
344
362
  "annotations": {
345
363
  "readOnlyHint": true
346
364
  },
365
+ "authorization": {
366
+ "approval": "never",
367
+ "audience": "owner",
368
+ "effects": [
369
+ "read"
370
+ ],
371
+ "requiredScopes": [
372
+ "voice:sessions:read"
373
+ ],
374
+ "resource": {
375
+ "idField": "id",
376
+ "type": "voice-session"
377
+ }
378
+ },
347
379
  "description": "Fetch one call session by id: status, timing, and the committed conversation turns (caller text and assistant reply), most recent last.",
348
380
  "input": {
349
381
  "type": "object",
@@ -368,6 +400,16 @@
368
400
  "annotations": {
369
401
  "readOnlyHint": true
370
402
  },
403
+ "authorization": {
404
+ "approval": "never",
405
+ "audience": "owner",
406
+ "effects": [
407
+ "read"
408
+ ],
409
+ "requiredScopes": [
410
+ "voice:sessions:read"
411
+ ]
412
+ },
371
413
  "description": "List call sessions with status, creation time, last activity, and turn count — most recently active first.",
372
414
  "input": {
373
415
  "type": "object",
@@ -7211,6 +7211,8 @@ var createVoiceSession = (options) => {
7211
7211
  user: 0
7212
7212
  };
7213
7213
  const recordingFormats = {};
7214
+ const recordingStartedAt = Date.now();
7215
+ let assistantRecordingEndsAtMs = 0;
7214
7216
  let recordingPersisted = false;
7215
7217
  const captureRecordingChunk = (channel, bytes2, format) => {
7216
7218
  if (!recordingConfig || recordingPersisted) {
@@ -7226,11 +7228,33 @@ var createVoiceSession = (options) => {
7226
7228
  if (currentTotal >= recordingMaxBytes) {
7227
7229
  return;
7228
7230
  }
7229
- const remaining = recordingMaxBytes - currentTotal;
7231
+ const bytesPerSample = 2;
7232
+ const bytesPerSecond = format.sampleRateHz * format.channels * bytesPerSample;
7233
+ let chunkStartMs = currentTotal / bytesPerSecond * 1000;
7234
+ if (channel === "assistant" || currentTotal === 0) {
7235
+ const wallClockOffsetMs = Math.max(0, Date.now() - recordingStartedAt);
7236
+ chunkStartMs = channel === "assistant" ? Math.max(wallClockOffsetMs, assistantRecordingEndsAtMs) : wallClockOffsetMs;
7237
+ const targetByteOffset = Math.floor(chunkStartMs * bytesPerSecond / 1000 / bytesPerSample) * bytesPerSample;
7238
+ const silenceByteLength = Math.min(Math.max(0, targetByteOffset - currentTotal), recordingMaxBytes - currentTotal);
7239
+ if (silenceByteLength > 0) {
7240
+ recordingBuffers[channel].push(new Uint8Array(silenceByteLength));
7241
+ recordingByteTotals[channel] += silenceByteLength;
7242
+ }
7243
+ if (channel === "assistant") {
7244
+ assistantRecordingEndsAtMs = chunkStartMs + bytes2.byteLength / bytesPerSecond * 1000;
7245
+ }
7246
+ }
7247
+ const updatedTotal = recordingByteTotals[channel];
7248
+ if (updatedTotal >= recordingMaxBytes) {
7249
+ recordingFormats[channel] = format;
7250
+ return;
7251
+ }
7252
+ const remaining = recordingMaxBytes - updatedTotal;
7230
7253
  const slice = bytes2.byteLength <= remaining ? bytes2 : bytes2.subarray(0, remaining);
7231
7254
  recordingBuffers[channel].push(new Uint8Array(slice));
7232
7255
  recordingByteTotals[channel] += slice.byteLength;
7233
7256
  recordingFormats[channel] = format;
7257
+ return { byteLength: slice.byteLength, startMs: chunkStartMs };
7234
7258
  };
7235
7259
  const pruneTurnAudio = () => {
7236
7260
  const replayWindowMs = sttFallback?.replayWindowMs ?? DEFAULT_FALLBACK_REPLAY_MS;
@@ -7558,7 +7582,7 @@ var createVoiceSession = (options) => {
7558
7582
  };
7559
7583
  const sendAssistantAudio = async (chunk, input) => {
7560
7584
  const normalizedChunk = chunk instanceof Uint8Array ? new Uint8Array(chunk) : chunk instanceof ArrayBuffer ? new Uint8Array(chunk.slice(0)) : new Uint8Array(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
7561
- captureRecordingChunk("assistant", normalizedChunk, input.format);
7585
+ const recordingChunk = captureRecordingChunk("assistant", normalizedChunk, input.format);
7562
7586
  kickCallSilenceWatchdog();
7563
7587
  await send({
7564
7588
  chunkBase64: encodeBase64(normalizedChunk),
@@ -7577,6 +7601,10 @@ var createVoiceSession = (options) => {
7577
7601
  if (activeTTSTurnId) {
7578
7602
  await appendTurnLatencyStage({
7579
7603
  at: input.receivedAt,
7604
+ metadata: recordingChunk ? {
7605
+ recordingByteLength: recordingChunk.byteLength,
7606
+ recordingStartMs: recordingChunk.startMs
7607
+ } : undefined,
7580
7608
  stage: "assistant_audio_received",
7581
7609
  turnId: activeTTSTurnId
7582
7610
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.643",
3
+ "version": "0.0.22-beta.645",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -140,13 +140,13 @@
140
140
  }
141
141
  },
142
142
  "dependencies": {
143
- "@absolutejs/manifest": "^0.1.0",
143
+ "@absolutejs/manifest": "^0.4.0",
144
144
  "@absolutejs/media": "0.0.1-beta.19",
145
145
  "@sinclair/typebox": "^0.34.0"
146
146
  },
147
147
  "devDependencies": {
148
148
  "@absolutejs/absolute": "0.19.0-beta.1009",
149
- "@absolutejs/ai": "0.0.5",
149
+ "@absolutejs/ai": "0.0.46",
150
150
  "@angular/core": "^21.0.0",
151
151
  "@electric-sql/pglite": "^0.4.5",
152
152
  "@eslint/js": "^10.0.1",
@@ -169,6 +169,6 @@
169
169
  "vue": "3.5.27"
170
170
  },
171
171
  "absolutejs": {
172
- "manifestContract": 1
172
+ "manifestContract": 2
173
173
  }
174
174
  }