@automagik/omni 2.260409.3 → 2.260409.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"done.d.ts","sourceRoot":"","sources":["../../src/commands/done.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2HpC,wBAAgB,iBAAiB,IAAI,OAAO,CA0C3C"}
1
+ {"version":3,"file":"done.d.ts","sourceRoot":"","sources":["../../src/commands/done.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmIpC,wBAAgB,iBAAiB,IAAI,OAAO,CA0C3C"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Turns Commands — Admin turn management
3
+ *
4
+ * omni turns list [--status open|done|timeout] [--instance <id>] [--chat <id>] [--agent <id>] [--limit N]
5
+ * omni turns get <id>
6
+ * omni turns close <id> [--reason <text>]
7
+ * omni turns close-all --confirm [--reason <text>]
8
+ * omni turns stats
9
+ */
10
+ import { Command } from 'commander';
11
+ export declare function createTurnsCommand(): Command;
12
+ //# sourceMappingURL=turns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turns.d.ts","sourceRoot":"","sources":["../../src/commands/turns.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,kBAAkB,IAAI,OAAO,CAiG5C"}
package/dist/index.js CHANGED
@@ -76815,16 +76815,82 @@ function createOmniClient(config) {
76815
76815
  }
76816
76816
  },
76817
76817
  turns: {
76818
- async close(body) {
76818
+ async close(body, extraHeaders) {
76819
76819
  const resp = await apiFetch(`${baseUrl}/api/v2/turns/close`, {
76820
76820
  method: "POST",
76821
- headers: { "Content-Type": "application/json" },
76821
+ headers: { "Content-Type": "application/json", ...extraHeaders },
76822
76822
  body: JSON.stringify(body)
76823
76823
  });
76824
76824
  const json = await resp.json();
76825
76825
  if (!resp.ok)
76826
76826
  throw OmniApiError.from(json, resp.status);
76827
76827
  return json?.data ?? {};
76828
+ },
76829
+ async list(params) {
76830
+ const qs = new URLSearchParams;
76831
+ if (params?.status)
76832
+ qs.set("status", params.status);
76833
+ if (params?.instanceId)
76834
+ qs.set("instanceId", params.instanceId);
76835
+ if (params?.chatId)
76836
+ qs.set("chatId", params.chatId);
76837
+ if (params?.agentId)
76838
+ qs.set("agentId", params.agentId);
76839
+ if (params?.limit !== undefined)
76840
+ qs.set("limit", String(params.limit));
76841
+ if (params?.offset !== undefined)
76842
+ qs.set("offset", String(params.offset));
76843
+ const query = qs.toString();
76844
+ const url = `${baseUrl}/api/v2/turns${query ? `?${query}` : ""}`;
76845
+ const resp = await apiFetch(url);
76846
+ const json = await resp.json();
76847
+ if (!resp.ok)
76848
+ throw OmniApiError.from(json, resp.status);
76849
+ return json?.data ?? { items: [], total: 0, limit: 50, offset: 0 };
76850
+ },
76851
+ async get(id) {
76852
+ const resp = await apiFetch(`${baseUrl}/api/v2/turns/${id}`);
76853
+ const json = await resp.json();
76854
+ if (!resp.ok)
76855
+ throw OmniApiError.from(json, resp.status);
76856
+ if (!json?.data)
76857
+ throw new OmniApiError("Turn not found", "NOT_FOUND", undefined, 404);
76858
+ return json.data;
76859
+ },
76860
+ async forceClose(id, reason) {
76861
+ const resp = await apiFetch(`${baseUrl}/api/v2/turns/${id}/close`, {
76862
+ method: "POST",
76863
+ headers: { "Content-Type": "application/json" },
76864
+ body: JSON.stringify({ reason })
76865
+ });
76866
+ const json = await resp.json();
76867
+ if (!resp.ok)
76868
+ throw OmniApiError.from(json, resp.status);
76869
+ if (!json?.data)
76870
+ throw new OmniApiError("Turn not found or already closed", "NOT_FOUND", undefined, 404);
76871
+ return json.data;
76872
+ },
76873
+ async bulkClose(reason) {
76874
+ const resp = await apiFetch(`${baseUrl}/api/v2/turns/close-all`, {
76875
+ method: "POST",
76876
+ headers: { "Content-Type": "application/json" },
76877
+ body: JSON.stringify({ confirm: true, reason })
76878
+ });
76879
+ const json = await resp.json();
76880
+ if (!resp.ok)
76881
+ throw OmniApiError.from(json, resp.status);
76882
+ if (!json?.data)
76883
+ throw new OmniApiError("Bulk close failed", "INTERNAL_ERROR", undefined, 500);
76884
+ return json.data;
76885
+ },
76886
+ async stats() {
76887
+ const resp = await apiFetch(`${baseUrl}/api/v2/turns/stats`);
76888
+ const json = await resp.json();
76889
+ if (!resp.ok)
76890
+ throw OmniApiError.from(json, resp.status);
76891
+ if (!json?.data)
76892
+ throw new OmniApiError("Stats unavailable", "INTERNAL_ERROR", undefined, 500);
76893
+ return json.data;
76828
76894
  }
76829
76895
  },
76830
76896
  agents: {
@@ -76906,7 +76972,7 @@ import { fileURLToPath } from "url";
76906
76972
  // package.json
76907
76973
  var package_default = {
76908
76974
  name: "@automagik/omni",
76909
- version: "2.260409.3",
76975
+ version: "2.260409.4",
76910
76976
  description: "LLM-optimized CLI for Omni",
76911
76977
  type: "module",
76912
76978
  bin: {
@@ -80700,7 +80766,13 @@ function getMediaType(path) {
80700
80766
  }
80701
80767
  async function closeTurn(client, action, successMsg, reason) {
80702
80768
  try {
80703
- const result = await client.turns.close({ action, reason });
80769
+ const turnId = process.env.OMNI_TURN_ID;
80770
+ const extraHeaders = {};
80771
+ if (process.env.OMNI_INSTANCE)
80772
+ extraHeaders["x-omni-instance"] = process.env.OMNI_INSTANCE;
80773
+ if (process.env.OMNI_CHAT)
80774
+ extraHeaders["x-omni-chat"] = process.env.OMNI_CHAT;
80775
+ const result = await client.turns.close({ action, reason, turnId }, extraHeaders);
80704
80776
  if (result.alreadyClosed) {
80705
80777
  success(`${successMsg} Turn already closed (idempotent).`);
80706
80778
  } else {
@@ -80790,7 +80862,7 @@ function createDoneCommand() {
80790
80862
  return handleMedia(client, ctxWithMessage, options.media, options.caption);
80791
80863
  if (text)
80792
80864
  return handleText(client, ctxWithMessage, text);
80793
- error('Specify what to do: omni done "text", omni done --media <file>, omni done --react <emoji>, or omni done --skip');
80865
+ return handleSkip(client, options.reason);
80794
80866
  });
80795
80867
  }
80796
80868
 
@@ -95594,6 +95666,78 @@ function createTtsCommand() {
95594
95666
  return tts;
95595
95667
  }
95596
95668
 
95669
+ // src/commands/turns.ts
95670
+ init_output();
95671
+ function formatDuration2(startedAt, closedAt) {
95672
+ const start = new Date(startedAt).getTime();
95673
+ const end = closedAt ? new Date(closedAt).getTime() : Date.now();
95674
+ const ms = end - start;
95675
+ if (ms < 1000)
95676
+ return `${ms}ms`;
95677
+ if (ms < 60000)
95678
+ return `${Math.round(ms / 1000)}s`;
95679
+ return `${Math.round(ms / 60000)}m`;
95680
+ }
95681
+ function createTurnsCommand() {
95682
+ const cmd = new Command("turns").description("Admin turn management");
95683
+ cmd.command("list").description("List turns with optional filters").option("--status <status>", "Filter by status (open, done, timeout)").option("--instance <id>", "Filter by instance ID").option("--chat <id>", "Filter by chat ID").option("--agent <id>", "Filter by agent ID").option("--limit <n>", "Max results (default 50)", "50").option("--offset <n>", "Offset for pagination (default 0)", "0").action(async (opts) => {
95684
+ const client = getClient();
95685
+ const result = await client.turns.list({
95686
+ status: opts.status,
95687
+ instanceId: opts.instance,
95688
+ chatId: opts.chat,
95689
+ agentId: opts.agent,
95690
+ limit: Number(opts.limit),
95691
+ offset: Number(opts.offset)
95692
+ });
95693
+ const rows = result.items.map((t) => ({
95694
+ id: t.id.slice(0, 8),
95695
+ status: t.status,
95696
+ chat: t.chatId.length > 20 ? `${t.chatId.slice(0, 20)}\u2026` : t.chatId,
95697
+ agent: t.agentId.slice(0, 8),
95698
+ duration: formatDuration2(t.startedAt, t.closedAt),
95699
+ nudges: t.nudgeCount,
95700
+ msgs: t.messagesSent
95701
+ }));
95702
+ list(rows, {
95703
+ emptyMessage: "No turns found.",
95704
+ rawData: result.items
95705
+ });
95706
+ if (getCurrentFormat() !== "json") {
95707
+ dim(`Total: ${result.total} | Showing ${result.offset + 1}\u2013${result.offset + result.items.length}`);
95708
+ }
95709
+ });
95710
+ cmd.command("get").description("Get a single turn by ID").argument("<id>", "Turn ID").action(async (id) => {
95711
+ const client = getClient();
95712
+ const turn = await client.turns.get(id);
95713
+ data(turn);
95714
+ });
95715
+ cmd.command("close").description("Admin force-close a turn").argument("<id>", "Turn ID to close").option("--reason <text>", "Close reason").action(async (id, opts) => {
95716
+ const client = getClient();
95717
+ const result = await client.turns.forceClose(id, opts.reason);
95718
+ success(`Turn ${result.turnId} closed`, result);
95719
+ });
95720
+ cmd.command("close-all").description("Bulk close all open turns (requires --confirm)").option("--confirm", "Confirm bulk close").option("--reason <text>", "Close reason").action(async (opts) => {
95721
+ if (!opts.confirm) {
95722
+ error("Bulk close requires --confirm flag to proceed.");
95723
+ }
95724
+ const client = getClient();
95725
+ const result = await client.turns.bulkClose(opts.reason);
95726
+ success(result.message, result);
95727
+ });
95728
+ cmd.command("stats").description("Show aggregate turn metrics").action(async () => {
95729
+ const client = getClient();
95730
+ const stats = await client.turns.stats();
95731
+ data({
95732
+ openCount: stats.openCount,
95733
+ totalCount: stats.totalCount,
95734
+ avgDurationMs: stats.avgDurationMs,
95735
+ timeoutRate: `${(stats.timeoutRate * 100).toFixed(1)}%`
95736
+ });
95737
+ });
95738
+ return cmd;
95739
+ }
95740
+
95597
95741
  // src/commands/update.ts
95598
95742
  import { createInterface as createInterface4 } from "readline";
95599
95743
  init_source();
@@ -96538,6 +96682,12 @@ var COMMANDS = [
96538
96682
  helpGroup: "Management",
96539
96683
  helpDescription: "Webhook management"
96540
96684
  },
96685
+ {
96686
+ create: createTurnsCommand,
96687
+ category: "advanced",
96688
+ helpGroup: "Management",
96689
+ helpDescription: "Admin turn management (list, close, stats)"
96690
+ },
96541
96691
  {
96542
96692
  create: createStatusCommand,
96543
96693
  category: "core",
@@ -222829,7 +222829,7 @@ var init_sentry_scrub = __esm(() => {
222829
222829
  var require_package8 = __commonJS((exports, module) => {
222830
222830
  module.exports = {
222831
222831
  name: "@omni/api",
222832
- version: "2.260409.3",
222832
+ version: "2.260409.4",
222833
222833
  type: "module",
222834
222834
  exports: {
222835
222835
  ".": {
@@ -238212,6 +238212,11 @@ var init_scopes = __esm(() => {
238212
238212
  "PATCH /settings": "settings:write",
238213
238213
  "DELETE /settings/:key": "settings:write",
238214
238214
  "GET /settings/:key/history": "settings:read",
238215
+ "GET /turns": "turns:admin",
238216
+ "GET /turns/stats": "turns:admin",
238217
+ "GET /turns/:id": "turns:admin",
238218
+ "POST /turns/:id/close": "turns:admin",
238219
+ "POST /turns/close-all": "turns:admin",
238215
238220
  "POST /turns/close": "turns:close",
238216
238221
  "GET /webhook-sources": "webhooks:read",
238217
238222
  "GET /webhook-sources/:id": "webhooks:read",
@@ -349377,6 +349382,63 @@ class TurnService {
349377
349382
  async incrementMessages(turnId) {
349378
349383
  await this.db.update(turns).set({ messagesSent: sql`${turns.messagesSent} + 1` }).where(eq(turns.id, turnId));
349379
349384
  }
349385
+ async list(options) {
349386
+ const conditions3 = [];
349387
+ if (options.status)
349388
+ conditions3.push(eq(turns.status, options.status));
349389
+ if (options.instanceId)
349390
+ conditions3.push(eq(turns.instanceId, options.instanceId));
349391
+ if (options.chatId)
349392
+ conditions3.push(eq(turns.chatId, options.chatId));
349393
+ if (options.agentId)
349394
+ conditions3.push(eq(turns.agentId, options.agentId));
349395
+ const where = conditions3.length > 0 ? and2(...conditions3) : undefined;
349396
+ const [items, [totalRow]] = await Promise.all([
349397
+ this.db.select().from(turns).where(where).orderBy(sql`${turns.startedAt} DESC`).limit(options.limit).offset(options.offset),
349398
+ this.db.select({ count: count2() }).from(turns).where(where)
349399
+ ]);
349400
+ return { items, total: totalRow?.count ?? 0 };
349401
+ }
349402
+ async stats() {
349403
+ const [row] = await this.db.select({
349404
+ totalCount: count2(),
349405
+ openCount: sql`count(*) filter (where ${turns.status} = 'open')`,
349406
+ timeoutCount: sql`count(*) filter (where ${turns.status} = 'timeout')`,
349407
+ avgDurationMs: sql`coalesce(avg(extract(epoch from (${turns.closedAt} - ${turns.startedAt})) * 1000) filter (where ${turns.closedAt} is not null), 0)`
349408
+ }).from(turns);
349409
+ const total = row?.totalCount ?? 0;
349410
+ const timeoutCount = Number(row?.timeoutCount ?? 0);
349411
+ return {
349412
+ openCount: Number(row?.openCount ?? 0),
349413
+ totalCount: total,
349414
+ avgDurationMs: Math.round(Number(row?.avgDurationMs ?? 0)),
349415
+ timeoutRate: total > 0 ? timeoutCount / total : 0
349416
+ };
349417
+ }
349418
+ async forceClose(turnId, reason) {
349419
+ const now = new Date;
349420
+ const [closed] = await this.db.update(turns).set({
349421
+ status: "done",
349422
+ action: "skip",
349423
+ closedAt: now,
349424
+ closedReason: reason ?? "admin force-close"
349425
+ }).where(and2(eq(turns.id, turnId), eq(turns.status, "open"))).returning();
349426
+ if (closed) {
349427
+ log83.info("Turn force-closed by admin", { turnId });
349428
+ }
349429
+ return closed ?? null;
349430
+ }
349431
+ async bulkClose(reason) {
349432
+ const now = new Date;
349433
+ const closed = await this.db.update(turns).set({
349434
+ status: "done",
349435
+ action: "skip",
349436
+ closedAt: now,
349437
+ closedReason: reason ?? "admin bulk close"
349438
+ }).where(eq(turns.status, "open")).returning({ id: turns.id });
349439
+ log83.info("Bulk close completed", { closedCount: closed.length });
349440
+ return closed.length;
349441
+ }
349380
349442
  }
349381
349443
  var log83;
349382
349444
  var init_turns = __esm(() => {
@@ -363490,16 +363552,51 @@ var init_turn_events = __esm(() => {
363490
363552
  });
363491
363553
 
363492
363554
  // ../api/src/routes/v2/turns.ts
363493
- var turnsRoutes, closeTurnSchema;
363555
+ function requireAdmin(c) {
363556
+ const keyData = c.get("apiKey");
363557
+ if (!keyData) {
363558
+ return c.json({ error: { code: "UNAUTHORIZED", message: "API key required" } }, 401);
363559
+ }
363560
+ if (!ApiKeyService.scopeAllows(keyData.scopes, "turns:admin")) {
363561
+ return c.json({ error: { code: "FORBIDDEN", message: "Master key or turns:admin scope required" } }, 403);
363562
+ }
363563
+ return null;
363564
+ }
363565
+ async function resolveOpenTurn(services, keyId, turnId, headerInstanceId, headerChatId) {
363566
+ const byKey = await services.turns.getOpenByApiKey(keyId);
363567
+ if (byKey)
363568
+ return byKey;
363569
+ if (turnId) {
363570
+ const candidate = await services.turns.getById(turnId);
363571
+ if (candidate && candidate.status === "open")
363572
+ return candidate;
363573
+ }
363574
+ let instanceId = headerInstanceId;
363575
+ let chatId = headerChatId;
363576
+ if (!instanceId || !chatId) {
363577
+ const fullKey = await services.apiKeys.getById(keyId);
363578
+ if (fullKey) {
363579
+ instanceId = instanceId ?? fullKey.contextInstanceId ?? undefined;
363580
+ chatId = chatId ?? fullKey.contextChatId ?? undefined;
363581
+ }
363582
+ }
363583
+ if (instanceId && chatId) {
363584
+ return services.turns.getOpen(instanceId, chatId);
363585
+ }
363586
+ return null;
363587
+ }
363588
+ var turnsRoutes, closeTurnSchema, listQuerySchema16, adminForceCloseSchema, bulkCloseSchema;
363494
363589
  var init_turns2 = __esm(() => {
363495
363590
  init_dist6();
363496
363591
  init_dist2();
363497
363592
  init_zod();
363593
+ init_api_keys();
363498
363594
  init_turn_events();
363499
363595
  turnsRoutes = new Hono2;
363500
363596
  closeTurnSchema = exports_external.object({
363501
363597
  action: exports_external.enum(["message", "react", "skip"]).describe("How the turn was closed"),
363502
- reason: exports_external.string().optional().describe("Close reason (for skip action)")
363598
+ reason: exports_external.string().optional().describe("Close reason (for skip action)"),
363599
+ turnId: exports_external.string().uuid().optional().describe("Explicit turn ID to close (fallback when API key has no associated turn)")
363503
363600
  });
363504
363601
  turnsRoutes.post("/close", zValidator("json", closeTurnSchema), async (c) => {
363505
363602
  const keyData = c.get("apiKey");
@@ -363508,12 +363605,12 @@ var init_turns2 = __esm(() => {
363508
363605
  }
363509
363606
  const body = c.req.valid("json");
363510
363607
  const services = c.get("services");
363511
- const openTurn = await services.turns.getOpenByApiKey(keyData.id);
363608
+ const openTurn = await resolveOpenTurn(services, keyData.id, body.turnId, c.req.header("x-omni-instance"), c.req.header("x-omni-chat"));
363512
363609
  if (!openTurn) {
363513
363610
  return c.json({
363514
363611
  data: {
363515
363612
  alreadyClosed: true,
363516
- message: "No open turn for this API key"
363613
+ message: "No open turn found"
363517
363614
  }
363518
363615
  });
363519
363616
  }
@@ -363551,10 +363648,145 @@ var init_turns2 = __esm(() => {
363551
363648
  }
363552
363649
  });
363553
363650
  });
363651
+ listQuerySchema16 = exports_external.object({
363652
+ status: exports_external.enum(["open", "done", "timeout"]).optional(),
363653
+ instanceId: exports_external.string().uuid().optional(),
363654
+ chatId: exports_external.string().optional(),
363655
+ agentId: exports_external.string().uuid().optional(),
363656
+ limit: exports_external.coerce.number().int().min(1).max(200).default(50),
363657
+ offset: exports_external.coerce.number().int().min(0).default(0)
363658
+ });
363659
+ adminForceCloseSchema = exports_external.object({
363660
+ reason: exports_external.string().optional()
363661
+ });
363662
+ bulkCloseSchema = exports_external.object({
363663
+ confirm: exports_external.boolean(),
363664
+ reason: exports_external.string().optional()
363665
+ });
363666
+ turnsRoutes.get("/", zValidator("query", listQuerySchema16), async (c) => {
363667
+ const denied = requireAdmin(c);
363668
+ if (denied)
363669
+ return denied;
363670
+ const query = c.req.valid("query");
363671
+ const services = c.get("services");
363672
+ const { items, total } = await services.turns.list({
363673
+ status: query.status,
363674
+ instanceId: query.instanceId,
363675
+ chatId: query.chatId,
363676
+ agentId: query.agentId,
363677
+ limit: query.limit,
363678
+ offset: query.offset
363679
+ });
363680
+ return c.json({
363681
+ data: {
363682
+ items: items.map((t) => ({
363683
+ id: t.id,
363684
+ instanceId: t.instanceId,
363685
+ chatId: t.chatId,
363686
+ messageId: t.messageId,
363687
+ agentId: t.agentId,
363688
+ apiKeyId: t.apiKeyId,
363689
+ status: t.status,
363690
+ action: t.action,
363691
+ nudgeCount: t.nudgeCount,
363692
+ messagesSent: t.messagesSent,
363693
+ startedAt: t.startedAt.toISOString(),
363694
+ lastActivityAt: t.lastActivityAt.toISOString(),
363695
+ closedAt: t.closedAt?.toISOString() ?? null,
363696
+ closedReason: t.closedReason
363697
+ })),
363698
+ total,
363699
+ limit: query.limit,
363700
+ offset: query.offset
363701
+ }
363702
+ });
363703
+ });
363704
+ turnsRoutes.get("/stats", async (c) => {
363705
+ const denied = requireAdmin(c);
363706
+ if (denied)
363707
+ return denied;
363708
+ const services = c.get("services");
363709
+ const stats = await services.turns.stats();
363710
+ return c.json({ data: stats });
363711
+ });
363712
+ turnsRoutes.get("/:id", async (c) => {
363713
+ const denied = requireAdmin(c);
363714
+ if (denied)
363715
+ return denied;
363716
+ const services = c.get("services");
363717
+ const turn = await services.turns.getById(c.req.param("id"));
363718
+ if (!turn) {
363719
+ return c.json({ error: { code: "NOT_FOUND", message: "Turn not found" } }, 404);
363720
+ }
363721
+ return c.json({
363722
+ data: {
363723
+ id: turn.id,
363724
+ instanceId: turn.instanceId,
363725
+ chatId: turn.chatId,
363726
+ messageId: turn.messageId,
363727
+ agentId: turn.agentId,
363728
+ apiKeyId: turn.apiKeyId,
363729
+ status: turn.status,
363730
+ action: turn.action,
363731
+ nudgeCount: turn.nudgeCount,
363732
+ messagesSent: turn.messagesSent,
363733
+ startedAt: turn.startedAt.toISOString(),
363734
+ lastActivityAt: turn.lastActivityAt.toISOString(),
363735
+ closedAt: turn.closedAt?.toISOString() ?? null,
363736
+ closedReason: turn.closedReason,
363737
+ metadata: turn.metadata
363738
+ }
363739
+ });
363740
+ });
363741
+ turnsRoutes.post("/:id/close", zValidator("json", adminForceCloseSchema), async (c) => {
363742
+ const denied = requireAdmin(c);
363743
+ if (denied)
363744
+ return denied;
363745
+ const services = c.get("services");
363746
+ const body = c.req.valid("json");
363747
+ const turnId = c.req.param("id");
363748
+ const closed = await services.turns.forceClose(turnId, body.reason);
363749
+ if (!closed) {
363750
+ return c.json({ error: { code: "NOT_FOUND", message: "Turn not found or already closed" } }, 404);
363751
+ }
363752
+ const durationMs = closed.closedAt ? closed.closedAt.getTime() - closed.startedAt.getTime() : Date.now() - closed.startedAt.getTime();
363753
+ publishTurnDone(closed.instanceId, closed.chatId, {
363754
+ turnId: closed.id,
363755
+ action: "skip",
363756
+ reason: body.reason ?? "admin force-close",
363757
+ duration: durationMs,
363758
+ nudgeCount: closed.nudgeCount,
363759
+ messagesSent: closed.messagesSent
363760
+ });
363761
+ return c.json({
363762
+ data: {
363763
+ turnId: closed.id,
363764
+ status: closed.status,
363765
+ closedAt: closed.closedAt?.toISOString() ?? null
363766
+ }
363767
+ });
363768
+ });
363769
+ turnsRoutes.post("/close-all", zValidator("json", bulkCloseSchema), async (c) => {
363770
+ const denied = requireAdmin(c);
363771
+ if (denied)
363772
+ return denied;
363773
+ const body = c.req.valid("json");
363774
+ if (!body.confirm) {
363775
+ return c.json({ error: { code: "CONFIRMATION_REQUIRED", message: "Set confirm: true to bulk-close all open turns" } }, 400);
363776
+ }
363777
+ const services = c.get("services");
363778
+ const closedCount = await services.turns.bulkClose(body.reason);
363779
+ return c.json({
363780
+ data: {
363781
+ closedCount,
363782
+ message: `Closed ${closedCount} open turn(s)`
363783
+ }
363784
+ });
363785
+ });
363554
363786
  });
363555
363787
 
363556
363788
  // ../api/src/routes/v2/webhooks.ts
363557
- var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema16, triggerEventSchema;
363789
+ var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema17, triggerEventSchema;
363558
363790
  var init_webhooks3 = __esm(() => {
363559
363791
  init_dist6();
363560
363792
  init_dist2();
@@ -363568,10 +363800,10 @@ var init_webhooks3 = __esm(() => {
363568
363800
  enabled: exports_external.boolean().default(true).describe("Whether source is enabled")
363569
363801
  });
363570
363802
  updateWebhookSourceSchema = createWebhookSourceSchema.partial();
363571
- listQuerySchema16 = exports_external.object({
363803
+ listQuerySchema17 = exports_external.object({
363572
363804
  enabled: exports_external.coerce.boolean().optional()
363573
363805
  });
363574
- webhooksRoutes.get("/webhook-sources", zValidator("query", listQuerySchema16), async (c) => {
363806
+ webhooksRoutes.get("/webhook-sources", zValidator("query", listQuerySchema17), async (c) => {
363575
363807
  const { enabled } = c.req.valid("query");
363576
363808
  const services = c.get("services");
363577
363809
  const sources = await services.webhooks.list({ enabled });
@@ -366652,10 +366884,13 @@ async function dispatchViaTurnBasedProvider(services, instance4, provider, trigg
366652
366884
  agentId: agentRecord.id,
366653
366885
  apiKeyId: scopedKey.id
366654
366886
  });
366887
+ const contextChat = await services.chats.findByExternalIdSmart(instance4.id, chatId);
366888
+ const contextChatId = contextChat?.id ?? null;
366889
+ const contextMsg = contextChat && messageId ? await services.messages.getByExternalId(contextChat.id, messageId) : null;
366655
366890
  await services.apiKeys.update(scopedKey.id, {
366656
366891
  contextInstanceId: instance4.id,
366657
- contextChatId: chatId,
366658
- contextMessageId: messageId || null
366892
+ contextChatId,
366893
+ contextMessageId: contextMsg?.id ?? null
366659
366894
  });
366660
366895
  publishTurnOpen(instance4.id, chatId, {
366661
366896
  turnId: turn.id,
@@ -367432,7 +367667,8 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
367432
367667
  const externalChatId = payload.chatId;
367433
367668
  const chat2 = await services.chats.findByExternalIdSmart(baseInstance.id, externalChatId);
367434
367669
  const internalChatId = chat2?.id;
367435
- const { instance: instance4, routeId: _routeId } = await resolveEffectiveInstance(services, db2, baseInstance, internalChatId, metadata.personId);
367670
+ const reactionPersonId = await resolvePersonId(services, channel4, baseInstance.id, payload.from, metadata.personId);
367671
+ const { instance: instance4, routeId: _routeId } = await resolveEffectiveInstance(services, db2, baseInstance, internalChatId, reactionPersonId);
367436
367672
  log98.info("Dispatching reaction trigger", {
367437
367673
  instanceId: instance4.id,
367438
367674
  chatId: externalChatId,
@@ -367445,7 +367681,8 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
367445
367681
  try {
367446
367682
  const provider = await getAgentProvider(services, instance4, db2);
367447
367683
  if (provider) {
367448
- const senderName2 = await services.agentRunner.getSenderName(metadata.personId, undefined);
367684
+ const effectivePersonId = reactionPersonId ?? metadata.personId;
367685
+ const senderName2 = await services.agentRunner.getSenderName(effectivePersonId, undefined);
367449
367686
  const sessionId = computeSessionId(instance4.agentSessionStrategy ?? "per_chat", payload.from, externalChatId);
367450
367687
  const trigger = {
367451
367688
  traceId: metadata.traceId,
@@ -367459,7 +367696,7 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
367459
367696
  },
367460
367697
  sender: {
367461
367698
  platformUserId: payload.from,
367462
- personId: metadata.personId,
367699
+ personId: effectivePersonId,
367463
367700
  displayName: senderName2
367464
367701
  },
367465
367702
  content: {
@@ -367902,7 +368139,9 @@ async function setupAgentDispatcher(eventBus, services, db2) {
367902
368139
  return;
367903
368140
  const traceId = metadata.traceId ?? generateCorrelationId("trc");
367904
368141
  const chat2 = await services.chats.findByExternalIdSmart(instance4.id, payload.chatId);
367905
- const { instance: resolved, routeId } = await resolveEffectiveInstance(services, db2, instance4, chat2?.id, metadata.personId);
368142
+ const channel4 = metadata.channelType ?? "whatsapp";
368143
+ const earlyPersonId = await resolvePersonId(services, channel4, instance4.id, payload.from, metadata.personId);
368144
+ const { instance: resolved, routeId } = await resolveEffectiveInstance(services, db2, instance4, chat2?.id, earlyPersonId);
367906
368145
  const debounceConfig = getDebounceConfig(resolved);
367907
368146
  const isGroupChat2 = payload.chatId.includes("@g.us");
367908
368147
  const effectiveDebounceConfig = isGroupChat2 && debounceConfig.groupMs != null ? {
@@ -368007,7 +368246,8 @@ async function setupAgentDispatcher(eventBus, services, db2) {
368007
368246
  if (!baseInstance?.agentId)
368008
368247
  return;
368009
368248
  const chat2 = await services.chats.findByExternalIdSmart(metadata.instanceId, payload.chatId);
368010
- const { instance: resolved } = await resolveEffectiveInstance(services, db2, baseInstance, chat2?.id, metadata.personId);
368249
+ const typingPersonId = metadata.personId;
368250
+ const { instance: resolved } = await resolveEffectiveInstance(services, db2, baseInstance, chat2?.id, typingPersonId);
368011
368251
  const debounceConfig = getDebounceConfig(resolved);
368012
368252
  if (debounceConfig.restartOnTyping) {
368013
368253
  debouncer.onUserTyping(metadata.instanceId, payload.chatId, debounceConfig);
@@ -369609,7 +369849,7 @@ class TurnMonitor {
369609
369849
  log102.info("Turn force-closed (timeout)", { turnId, duration, nudgeCount });
369610
369850
  }
369611
369851
  }
369612
- var log102, NUDGE_THRESHOLD_MS = 60000, FALLBACK_THRESHOLD_MS = 300000, TIMEOUT_THRESHOLD_MS = 900000, POLL_INTERVAL_MS2 = 1e4;
369852
+ var log102, NUDGE_THRESHOLD_MS = 120000, FALLBACK_THRESHOLD_MS = 600000, TIMEOUT_THRESHOLD_MS = 1800000, POLL_INTERVAL_MS2 = 1e4;
369613
369853
  var init_turn_monitor = __esm(() => {
369614
369854
  init_src();
369615
369855
  init_turn_events();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260409.3",
3
+ "version": "2.260409.4",
4
4
  "description": "LLM-optimized CLI for Omni",
5
5
  "type": "module",
6
6
  "bin": {