@agent-native/core 0.119.4 → 0.119.6

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.
Files changed (150) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/durable-background.ts +1 -0
  5. package/corpus/core/src/agent/engine/index.ts +2 -0
  6. package/corpus/core/src/agent/engine/registry.ts +95 -2
  7. package/corpus/core/src/deploy/build.ts +144 -2
  8. package/corpus/core/src/deploy/workspace-deploy.ts +123 -1
  9. package/corpus/core/src/integrations/integration-durable-dispatch-config.ts +22 -0
  10. package/corpus/core/src/integrations/integration-durable-dispatch.ts +190 -0
  11. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +175 -87
  12. package/corpus/core/src/integrations/pending-tasks-store.ts +114 -11
  13. package/corpus/core/src/integrations/plugin.ts +113 -60
  14. package/corpus/core/src/integrations/task-queue-stats.ts +68 -15
  15. package/corpus/core/src/integrations/webhook-handler.ts +21 -46
  16. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +8 -0
  17. package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +5 -1
  18. package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +9 -1
  19. package/corpus/core/src/server/auth.ts +6 -0
  20. package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
  21. package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
  22. package/corpus/core/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
  23. package/corpus/templates/dispatch/changelog/2026-07-21-recover-stalled-slack-agent-work.md +6 -0
  24. package/corpus/templates/slides/actions/patch-deck.ts +2 -1
  25. package/corpus/templates/slides/changelog/2026-07-23-opening-or-refining-a-deck-no-longer-crashes-with-a-blank-er.md +6 -0
  26. package/corpus/templates/slides/shared/aspect-ratios.ts +4 -1
  27. package/corpus/templates/tasks/.oxfmtrc.json +8 -0
  28. package/corpus/templates/tasks/AGENTS.md +34 -41
  29. package/corpus/templates/tasks/README.md +13 -6
  30. package/corpus/templates/tasks/actions/bulk-delete-inbox-items.ts +10 -8
  31. package/corpus/templates/tasks/actions/bulk-delete-tasks.ts +10 -8
  32. package/corpus/templates/tasks/actions/bulk-mark-inbox-items-ready.ts +1 -1
  33. package/corpus/templates/tasks/actions/bulk-update-tasks.ts +12 -10
  34. package/corpus/templates/tasks/actions/create-inbox-item.ts +7 -4
  35. package/corpus/templates/tasks/actions/create-task.ts +2 -1
  36. package/corpus/templates/tasks/actions/delete-inbox-item.ts +6 -4
  37. package/corpus/templates/tasks/actions/delete-task.ts +1 -1
  38. package/corpus/templates/tasks/actions/list-inbox-items.ts +4 -2
  39. package/corpus/templates/tasks/actions/list-visible-task-fields.ts +3 -1
  40. package/corpus/templates/tasks/actions/mark-inbox-item-ready.ts +6 -4
  41. package/corpus/templates/tasks/actions/navigate.ts +16 -14
  42. package/corpus/templates/tasks/actions/reorder-inbox-items.ts +9 -7
  43. package/corpus/templates/tasks/actions/update-inbox-item.ts +6 -4
  44. package/corpus/templates/tasks/actions/update-task.ts +12 -9
  45. package/corpus/templates/tasks/actions/update-visible-task-fields.ts +10 -8
  46. package/corpus/templates/tasks/actions/view-screen.ts +1 -1
  47. package/corpus/templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx +15 -0
  48. package/corpus/templates/tasks/changelog/2026-07-23-task-detail-extensions.md +6 -0
  49. package/corpus/templates/tasks/drizzle.config.ts +3 -0
  50. package/corpus/templates/tasks/package.json +4 -1
  51. package/corpus/templates/tasks/server/db/schema.ts +0 -10
  52. package/corpus/templates/tasks/server/db/test-tasks-table.ts +0 -5
  53. package/corpus/templates/tasks/server/plugins/agent-chat.ts +1 -1
  54. package/corpus/templates/tasks/server/plugins/db.ts +43 -10
  55. package/corpus/templates/tasks/server/user-config/store.ts +42 -81
  56. package/corpus/templates/tasks/tsconfig.json +1 -2
  57. package/corpus/templates/tasks/vitest.config.ts +10 -1
  58. package/dist/agent/durable-background.d.ts +1 -0
  59. package/dist/agent/durable-background.d.ts.map +1 -1
  60. package/dist/agent/durable-background.js +1 -0
  61. package/dist/agent/durable-background.js.map +1 -1
  62. package/dist/agent/engine/index.d.ts +1 -1
  63. package/dist/agent/engine/index.d.ts.map +1 -1
  64. package/dist/agent/engine/index.js +1 -1
  65. package/dist/agent/engine/index.js.map +1 -1
  66. package/dist/agent/engine/registry.d.ts +32 -1
  67. package/dist/agent/engine/registry.d.ts.map +1 -1
  68. package/dist/agent/engine/registry.js +75 -3
  69. package/dist/agent/engine/registry.js.map +1 -1
  70. package/dist/deploy/build.d.ts +3 -0
  71. package/dist/deploy/build.d.ts.map +1 -1
  72. package/dist/deploy/build.js +106 -3
  73. package/dist/deploy/build.js.map +1 -1
  74. package/dist/deploy/workspace-deploy.d.ts.map +1 -1
  75. package/dist/deploy/workspace-deploy.js +103 -2
  76. package/dist/deploy/workspace-deploy.js.map +1 -1
  77. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  78. package/dist/integrations/integration-durable-dispatch-config.d.ts +7 -0
  79. package/dist/integrations/integration-durable-dispatch-config.d.ts.map +1 -0
  80. package/dist/integrations/integration-durable-dispatch-config.js +16 -0
  81. package/dist/integrations/integration-durable-dispatch-config.js.map +1 -0
  82. package/dist/integrations/integration-durable-dispatch.d.ts +23 -0
  83. package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -0
  84. package/dist/integrations/integration-durable-dispatch.js +115 -0
  85. package/dist/integrations/integration-durable-dispatch.js.map +1 -0
  86. package/dist/integrations/pending-tasks-retry-job.d.ts +13 -1
  87. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  88. package/dist/integrations/pending-tasks-retry-job.js +133 -74
  89. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  90. package/dist/integrations/pending-tasks-store.d.ts +14 -2
  91. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  92. package/dist/integrations/pending-tasks-store.js +75 -11
  93. package/dist/integrations/pending-tasks-store.js.map +1 -1
  94. package/dist/integrations/plugin.d.ts.map +1 -1
  95. package/dist/integrations/plugin.js +88 -55
  96. package/dist/integrations/plugin.js.map +1 -1
  97. package/dist/integrations/task-queue-stats.d.ts +15 -3
  98. package/dist/integrations/task-queue-stats.d.ts.map +1 -1
  99. package/dist/integrations/task-queue-stats.js +45 -15
  100. package/dist/integrations/task-queue-stats.js.map +1 -1
  101. package/dist/integrations/webhook-handler.d.ts +1 -1
  102. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  103. package/dist/integrations/webhook-handler.js +18 -42
  104. package/dist/integrations/webhook-handler.js.map +1 -1
  105. package/dist/notifications/routes.d.ts +2 -2
  106. package/dist/observability/routes.d.ts +5 -5
  107. package/dist/progress/routes.d.ts +1 -1
  108. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  109. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  110. package/dist/resources/handlers.d.ts +1 -1
  111. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  112. package/dist/scripts/agent-engines/list-agent-engines.js +8 -2
  113. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  114. package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
  115. package/dist/scripts/agent-engines/manage-agent-engine.js +5 -2
  116. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  117. package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
  118. package/dist/scripts/agent-engines/set-agent-engine.js +9 -2
  119. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  120. package/dist/secrets/routes.d.ts +3 -3
  121. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  122. package/dist/server/auth.d.ts.map +1 -1
  123. package/dist/server/auth.js +5 -0
  124. package/dist/server/auth.js.map +1 -1
  125. package/dist/server/realtime-token.d.ts +1 -1
  126. package/dist/server/transcribe-voice.d.ts +1 -1
  127. package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
  128. package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
  129. package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
  130. package/package.json +1 -1
  131. package/src/agent/durable-background.ts +1 -0
  132. package/src/agent/engine/index.ts +2 -0
  133. package/src/agent/engine/registry.ts +95 -2
  134. package/src/deploy/build.ts +144 -2
  135. package/src/deploy/workspace-deploy.ts +123 -1
  136. package/src/integrations/integration-durable-dispatch-config.ts +22 -0
  137. package/src/integrations/integration-durable-dispatch.ts +190 -0
  138. package/src/integrations/pending-tasks-retry-job.ts +175 -87
  139. package/src/integrations/pending-tasks-store.ts +114 -11
  140. package/src/integrations/plugin.ts +113 -60
  141. package/src/integrations/task-queue-stats.ts +68 -15
  142. package/src/integrations/webhook-handler.ts +21 -46
  143. package/src/scripts/agent-engines/list-agent-engines.ts +8 -0
  144. package/src/scripts/agent-engines/manage-agent-engine.ts +5 -1
  145. package/src/scripts/agent-engines/set-agent-engine.ts +9 -1
  146. package/src/server/auth.ts +6 -0
  147. package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
  148. package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
  149. package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
  150. package/corpus/templates/tasks/.prettierrc +0 -5
@@ -11,12 +11,24 @@ export interface TaskQueueStats {
11
11
  failed_last_hour: number;
12
12
  oldest_pending_age_seconds: number;
13
13
  recent_failures: RecentFailure[];
14
+ recent_tasks: Array<{
15
+ id: string;
16
+ platform: string;
17
+ status: string;
18
+ attempts: number;
19
+ dispatch_attempts: number;
20
+ last_dispatch_outcome: string | null;
21
+ age_seconds: number;
22
+ }>;
23
+ }
24
+ export interface TaskQueueStatsScope {
25
+ ownerEmail: string;
26
+ orgId: string | null;
14
27
  }
15
28
  /**
16
29
  * Get a snapshot of the integration task queue health.
17
30
  *
18
- * Returns zeros if the table doesn't exist yet safe to call before the
19
- * pending-tasks store has initialised the schema.
31
+ * Safe to call before the pending-tasks store has initialized the schema.
20
32
  */
21
- export declare function getTaskQueueStats(): Promise<TaskQueueStats>;
33
+ export declare function getTaskQueueStats(scope: TaskQueueStatsScope): Promise<TaskQueueStats>;
22
34
  //# sourceMappingURL=task-queue-stats.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"task-queue-stats.d.ts","sourceRoot":"","sources":["../../src/integrations/task-queue-stats.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,0BAA0B,EAAE,MAAM,CAAC;IACnC,eAAe,EAAE,aAAa,EAAE,CAAC;CAClC;AAkBD;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CA8FjE"}
1
+ {"version":3,"file":"task-queue-stats.d.ts","sourceRoot":"","sources":["../../src/integrations/task-queue-stats.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,0BAA0B,EAAE,MAAM,CAAC;IACnC,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,YAAY,EAAE,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;QACrC,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAmBD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,cAAc,CAAC,CAkIzB"}
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * Lives in its own file so it stays out of `pending-tasks-store.ts`, which is
5
5
  * actively being edited by the agent that owns the queue itself. These
6
- * helpers only SELECT they never write and they degrade gracefully if
7
- * the `integration_pending_tasks` table doesn't exist yet (returning zeroed
8
- * stats instead of throwing).
6
+ * Queue reads never expose payloads or user text. The helper first runs the
7
+ * additive schema guard so older deployments gain the dispatch diagnostic
8
+ * columns before the SELECTs execute.
9
9
  */
10
10
  import { getDbExec } from "../db/client.js";
11
+ import { ensurePendingTasksTable } from "./pending-tasks-store.js";
11
12
  const ZERO_STATS = {
12
13
  pending: 0,
13
14
  processing: 0,
@@ -15,6 +16,7 @@ const ZERO_STATS = {
15
16
  failed_last_hour: 0,
16
17
  oldest_pending_age_seconds: 0,
17
18
  recent_failures: [],
19
+ recent_tasks: [],
18
20
  };
19
21
  function isMissingTableError(err) {
20
22
  const msg = err instanceof Error ? err.message : String(err ?? "");
@@ -23,20 +25,24 @@ function isMissingTableError(err) {
23
25
  /**
24
26
  * Get a snapshot of the integration task queue health.
25
27
  *
26
- * Returns zeros if the table doesn't exist yet safe to call before the
27
- * pending-tasks store has initialised the schema.
28
+ * Safe to call before the pending-tasks store has initialized the schema.
28
29
  */
29
- export async function getTaskQueueStats() {
30
+ export async function getTaskQueueStats(scope) {
31
+ await ensurePendingTasksTable();
30
32
  const client = getDbExec();
31
33
  const now = Date.now();
32
34
  const oneHourAgo = now - 60 * 60 * 1000;
35
+ const scopeSql = `owner_email = ?
36
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)`;
37
+ const scopeArgs = [scope.ownerEmail, scope.orgId, scope.orgId];
33
38
  try {
34
39
  // Status counts (pending, processing) — only need the live ones.
35
40
  const liveCounts = await client.execute({
36
41
  sql: `SELECT status, COUNT(*) AS c FROM integration_pending_tasks
37
- WHERE status IN ('pending', 'processing')
42
+ WHERE ${scopeSql}
43
+ AND status IN ('pending', 'processing')
38
44
  GROUP BY status`,
39
- args: [],
45
+ args: scopeArgs,
40
46
  });
41
47
  let pending = 0;
42
48
  let processing = 0;
@@ -53,9 +59,10 @@ export async function getTaskQueueStats() {
53
59
  // miss tasks queued >1h ago that just finished now.
54
60
  const lastHourCounts = await client.execute({
55
61
  sql: `SELECT status, COUNT(*) AS c FROM integration_pending_tasks
56
- WHERE status IN ('completed', 'failed') AND updated_at >= ?
62
+ WHERE ${scopeSql}
63
+ AND status IN ('completed', 'failed') AND updated_at >= ?
57
64
  GROUP BY status`,
58
- args: [oneHourAgo],
65
+ args: [...scopeArgs, oneHourAgo],
59
66
  });
60
67
  let completedLastHour = 0;
61
68
  let failedLastHour = 0;
@@ -72,10 +79,11 @@ export async function getTaskQueueStats() {
72
79
  if (pending > 0) {
73
80
  const oldest = await client.execute({
74
81
  sql: `SELECT created_at FROM integration_pending_tasks
75
- WHERE status = 'pending'
82
+ WHERE ${scopeSql}
83
+ AND status = 'pending'
76
84
  ORDER BY created_at ASC
77
85
  LIMIT 1`,
78
- args: [],
86
+ args: scopeArgs,
79
87
  });
80
88
  const oldestRow = oldest.rows[0];
81
89
  if (oldestRow) {
@@ -87,10 +95,11 @@ export async function getTaskQueueStats() {
87
95
  // blowing up the response payload.
88
96
  const failures = await client.execute({
89
97
  sql: `SELECT id, platform, error_message, attempts FROM integration_pending_tasks
90
- WHERE status = 'failed' AND updated_at >= ?
98
+ WHERE ${scopeSql}
99
+ AND status = 'failed' AND updated_at >= ?
91
100
  ORDER BY updated_at DESC
92
101
  LIMIT 5`,
93
- args: [oneHourAgo],
102
+ args: [...scopeArgs, oneHourAgo],
94
103
  });
95
104
  const recentFailures = failures.rows.map((row) => ({
96
105
  id: String(row.id ?? ""),
@@ -98,6 +107,26 @@ export async function getTaskQueueStats() {
98
107
  error: String(row.error_message ?? ""),
99
108
  attempts: Number(row.attempts ?? 0),
100
109
  }));
110
+ const recent = await client.execute({
111
+ sql: `SELECT id, platform, status, attempts, dispatch_attempts,
112
+ last_dispatch_outcome, created_at
113
+ FROM integration_pending_tasks
114
+ WHERE ${scopeSql}
115
+ ORDER BY created_at DESC
116
+ LIMIT 10`,
117
+ args: scopeArgs,
118
+ });
119
+ const recentTasks = recent.rows.map((row) => ({
120
+ id: String(row.id ?? ""),
121
+ platform: String(row.platform ?? ""),
122
+ status: String(row.status ?? ""),
123
+ attempts: Number(row.attempts ?? 0),
124
+ dispatch_attempts: Number(row.dispatch_attempts ?? 0),
125
+ last_dispatch_outcome: row.last_dispatch_outcome == null
126
+ ? null
127
+ : String(row.last_dispatch_outcome),
128
+ age_seconds: Math.max(0, Math.floor((now - Number(row.created_at ?? now)) / 1000)),
129
+ }));
101
130
  return {
102
131
  pending,
103
132
  processing,
@@ -105,11 +134,12 @@ export async function getTaskQueueStats() {
105
134
  failed_last_hour: failedLastHour,
106
135
  oldest_pending_age_seconds: oldestPendingAgeSeconds,
107
136
  recent_failures: recentFailures,
137
+ recent_tasks: recentTasks,
108
138
  };
109
139
  }
110
140
  catch (err) {
111
141
  if (isMissingTableError(err)) {
112
- return { ...ZERO_STATS, recent_failures: [] };
142
+ return { ...ZERO_STATS, recent_failures: [], recent_tasks: [] };
113
143
  }
114
144
  throw err;
115
145
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-queue-stats.js","sourceRoot":"","sources":["../../src/integrations/task-queue-stats.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Read-only observability helpers for the integration task queue.\n *\n * Lives in its own file so it stays out of `pending-tasks-store.ts`, which is\n * actively being edited by the agent that owns the queue itself. These\n * helpers only SELECT — they never write — and they degrade gracefully if\n * the `integration_pending_tasks` table doesn't exist yet (returning zeroed\n * stats instead of throwing).\n */\nimport { getDbExec } from \"../db/client.js\";\n\nexport interface RecentFailure {\n id: string;\n platform: string;\n error: string;\n attempts: number;\n}\n\nexport interface TaskQueueStats {\n pending: number;\n processing: number;\n completed_last_hour: number;\n failed_last_hour: number;\n oldest_pending_age_seconds: number;\n recent_failures: RecentFailure[];\n}\n\nconst ZERO_STATS: TaskQueueStats = {\n pending: 0,\n processing: 0,\n completed_last_hour: 0,\n failed_last_hour: 0,\n oldest_pending_age_seconds: 0,\n recent_failures: [],\n};\n\nfunction isMissingTableError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err ?? \"\");\n return /no such table|does not exist|relation .* does not exist|undefined_table/i.test(\n msg,\n );\n}\n\n/**\n * Get a snapshot of the integration task queue health.\n *\n * Returns zeros if the table doesn't exist yet — safe to call before the\n * pending-tasks store has initialised the schema.\n */\nexport async function getTaskQueueStats(): Promise<TaskQueueStats> {\n const client = getDbExec();\n const now = Date.now();\n const oneHourAgo = now - 60 * 60 * 1000;\n\n try {\n // Status counts (pending, processing) — only need the live ones.\n const liveCounts = await client.execute({\n sql: `SELECT status, COUNT(*) AS c FROM integration_pending_tasks\n WHERE status IN ('pending', 'processing')\n GROUP BY status`,\n args: [],\n });\n\n let pending = 0;\n let processing = 0;\n for (const row of liveCounts.rows as Array<Record<string, unknown>>) {\n const status = row.status as string;\n const count = Number(row.c ?? 0);\n if (status === \"pending\") pending = count;\n else if (status === \"processing\") processing = count;\n }\n\n // Last-hour completion + failure counts. updated_at is the most reliable\n // column — completed_at can be null on failed tasks, and created_at would\n // miss tasks queued >1h ago that just finished now.\n const lastHourCounts = await client.execute({\n sql: `SELECT status, COUNT(*) AS c FROM integration_pending_tasks\n WHERE status IN ('completed', 'failed') AND updated_at >= ?\n GROUP BY status`,\n args: [oneHourAgo],\n });\n\n let completedLastHour = 0;\n let failedLastHour = 0;\n for (const row of lastHourCounts.rows as Array<Record<string, unknown>>) {\n const status = row.status as string;\n const count = Number(row.c ?? 0);\n if (status === \"completed\") completedLastHour = count;\n else if (status === \"failed\") failedLastHour = count;\n }\n\n // Oldest pending task — used to surface stuck queues.\n let oldestPendingAgeSeconds = 0;\n if (pending > 0) {\n const oldest = await client.execute({\n sql: `SELECT created_at FROM integration_pending_tasks\n WHERE status = 'pending'\n ORDER BY created_at ASC\n LIMIT 1`,\n args: [],\n });\n const oldestRow = oldest.rows[0] as Record<string, unknown> | undefined;\n if (oldestRow) {\n const createdAt = Number(oldestRow.created_at ?? now);\n oldestPendingAgeSeconds = Math.max(\n 0,\n Math.floor((now - createdAt) / 1000),\n );\n }\n }\n\n // Recent failures, capped at 5 — enough to spot patterns without\n // blowing up the response payload.\n const failures = await client.execute({\n sql: `SELECT id, platform, error_message, attempts FROM integration_pending_tasks\n WHERE status = 'failed' AND updated_at >= ?\n ORDER BY updated_at DESC\n LIMIT 5`,\n args: [oneHourAgo],\n });\n const recentFailures: RecentFailure[] = (\n failures.rows as Array<Record<string, unknown>>\n ).map((row) => ({\n id: String(row.id ?? \"\"),\n platform: String(row.platform ?? \"\"),\n error: String(row.error_message ?? \"\"),\n attempts: Number(row.attempts ?? 0),\n }));\n\n return {\n pending,\n processing,\n completed_last_hour: completedLastHour,\n failed_last_hour: failedLastHour,\n oldest_pending_age_seconds: oldestPendingAgeSeconds,\n recent_failures: recentFailures,\n };\n } catch (err) {\n if (isMissingTableError(err)) {\n return { ...ZERO_STATS, recent_failures: [] };\n }\n throw err;\n }\n}\n"]}
1
+ {"version":3,"file":"task-queue-stats.js","sourceRoot":"","sources":["../../src/integrations/task-queue-stats.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Read-only observability helpers for the integration task queue.\n *\n * Lives in its own file so it stays out of `pending-tasks-store.ts`, which is\n * actively being edited by the agent that owns the queue itself. These\n * Queue reads never expose payloads or user text. The helper first runs the\n * additive schema guard so older deployments gain the dispatch diagnostic\n * columns before the SELECTs execute.\n */\nimport { getDbExec } from \"../db/client.js\";\nimport { ensurePendingTasksTable } from \"./pending-tasks-store.js\";\n\nexport interface RecentFailure {\n id: string;\n platform: string;\n error: string;\n attempts: number;\n}\n\nexport interface TaskQueueStats {\n pending: number;\n processing: number;\n completed_last_hour: number;\n failed_last_hour: number;\n oldest_pending_age_seconds: number;\n recent_failures: RecentFailure[];\n recent_tasks: Array<{\n id: string;\n platform: string;\n status: string;\n attempts: number;\n dispatch_attempts: number;\n last_dispatch_outcome: string | null;\n age_seconds: number;\n }>;\n}\n\nexport interface TaskQueueStatsScope {\n ownerEmail: string;\n orgId: string | null;\n}\n\nconst ZERO_STATS: TaskQueueStats = {\n pending: 0,\n processing: 0,\n completed_last_hour: 0,\n failed_last_hour: 0,\n oldest_pending_age_seconds: 0,\n recent_failures: [],\n recent_tasks: [],\n};\n\nfunction isMissingTableError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err ?? \"\");\n return /no such table|does not exist|relation .* does not exist|undefined_table/i.test(\n msg,\n );\n}\n\n/**\n * Get a snapshot of the integration task queue health.\n *\n * Safe to call before the pending-tasks store has initialized the schema.\n */\nexport async function getTaskQueueStats(\n scope: TaskQueueStatsScope,\n): Promise<TaskQueueStats> {\n await ensurePendingTasksTable();\n const client = getDbExec();\n const now = Date.now();\n const oneHourAgo = now - 60 * 60 * 1000;\n const scopeSql = `owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)`;\n const scopeArgs = [scope.ownerEmail, scope.orgId, scope.orgId];\n\n try {\n // Status counts (pending, processing) — only need the live ones.\n const liveCounts = await client.execute({\n sql: `SELECT status, COUNT(*) AS c FROM integration_pending_tasks\n WHERE ${scopeSql}\n AND status IN ('pending', 'processing')\n GROUP BY status`,\n args: scopeArgs,\n });\n\n let pending = 0;\n let processing = 0;\n for (const row of liveCounts.rows as Array<Record<string, unknown>>) {\n const status = row.status as string;\n const count = Number(row.c ?? 0);\n if (status === \"pending\") pending = count;\n else if (status === \"processing\") processing = count;\n }\n\n // Last-hour completion + failure counts. updated_at is the most reliable\n // column — completed_at can be null on failed tasks, and created_at would\n // miss tasks queued >1h ago that just finished now.\n const lastHourCounts = await client.execute({\n sql: `SELECT status, COUNT(*) AS c FROM integration_pending_tasks\n WHERE ${scopeSql}\n AND status IN ('completed', 'failed') AND updated_at >= ?\n GROUP BY status`,\n args: [...scopeArgs, oneHourAgo],\n });\n\n let completedLastHour = 0;\n let failedLastHour = 0;\n for (const row of lastHourCounts.rows as Array<Record<string, unknown>>) {\n const status = row.status as string;\n const count = Number(row.c ?? 0);\n if (status === \"completed\") completedLastHour = count;\n else if (status === \"failed\") failedLastHour = count;\n }\n\n // Oldest pending task — used to surface stuck queues.\n let oldestPendingAgeSeconds = 0;\n if (pending > 0) {\n const oldest = await client.execute({\n sql: `SELECT created_at FROM integration_pending_tasks\n WHERE ${scopeSql}\n AND status = 'pending'\n ORDER BY created_at ASC\n LIMIT 1`,\n args: scopeArgs,\n });\n const oldestRow = oldest.rows[0] as Record<string, unknown> | undefined;\n if (oldestRow) {\n const createdAt = Number(oldestRow.created_at ?? now);\n oldestPendingAgeSeconds = Math.max(\n 0,\n Math.floor((now - createdAt) / 1000),\n );\n }\n }\n\n // Recent failures, capped at 5 — enough to spot patterns without\n // blowing up the response payload.\n const failures = await client.execute({\n sql: `SELECT id, platform, error_message, attempts FROM integration_pending_tasks\n WHERE ${scopeSql}\n AND status = 'failed' AND updated_at >= ?\n ORDER BY updated_at DESC\n LIMIT 5`,\n args: [...scopeArgs, oneHourAgo],\n });\n const recentFailures: RecentFailure[] = (\n failures.rows as Array<Record<string, unknown>>\n ).map((row) => ({\n id: String(row.id ?? \"\"),\n platform: String(row.platform ?? \"\"),\n error: String(row.error_message ?? \"\"),\n attempts: Number(row.attempts ?? 0),\n }));\n\n const recent = await client.execute({\n sql: `SELECT id, platform, status, attempts, dispatch_attempts,\n last_dispatch_outcome, created_at\n FROM integration_pending_tasks\n WHERE ${scopeSql}\n ORDER BY created_at DESC\n LIMIT 10`,\n args: scopeArgs,\n });\n const recentTasks = (recent.rows as Array<Record<string, unknown>>).map(\n (row) => ({\n id: String(row.id ?? \"\"),\n platform: String(row.platform ?? \"\"),\n status: String(row.status ?? \"\"),\n attempts: Number(row.attempts ?? 0),\n dispatch_attempts: Number(row.dispatch_attempts ?? 0),\n last_dispatch_outcome:\n row.last_dispatch_outcome == null\n ? null\n : String(row.last_dispatch_outcome),\n age_seconds: Math.max(\n 0,\n Math.floor((now - Number(row.created_at ?? now)) / 1000),\n ),\n }),\n );\n\n return {\n pending,\n processing,\n completed_last_hour: completedLastHour,\n failed_last_hour: failedLastHour,\n oldest_pending_age_seconds: oldestPendingAgeSeconds,\n recent_failures: recentFailures,\n recent_tasks: recentTasks,\n };\n } catch (err) {\n if (isMissingTableError(err)) {\n return { ...ZERO_STATS, recent_failures: [], recent_tasks: [] };\n }\n throw err;\n }\n}\n"]}
@@ -81,7 +81,7 @@ export declare function resolveIntegrationApiKey(engineOption: WebhookHandlerOpt
81
81
  * 2. Verify webhook signature
82
82
  * 3. Parse incoming message (null = ignored event)
83
83
  * 4. Persist task to SQL
84
- * 5. Fire-and-forget POST to /_agent-native/integrations/process-task
84
+ * 5. Dispatch the queued task through the configured processor handoff
85
85
  * (a fresh function execution with its own timeout budget)
86
86
  * 6. Return HTTP 200 immediately (within Slack's 3s SLA)
87
87
  *
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAElC,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,6BAA6B,CAAC;AAcrC,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAOL,KAAK,WAAW,EACjB,MAAM,8BAA8B,CAAC;AA+BtC,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAsBpB,MAAM,MAAM,sCAAsC,GAAG;IACnD,IAAI,EAAE,mBAAmB,CAAC;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GACpC;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IACE,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,sCAAsC,CAAC;IAChD,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AA6CN,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,CAAC;IACzB,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,CAAC,EACH,WAAW,GACX,MAAM,GACN;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IACtD,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,yEAAyE;IACzE,aAAa,CAAC,EAAE,CACd,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,eAAe,KACrB,OAAO,CACR;QACE,OAAO,EAAE,IAAI,CAAC;QACd,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GACD;QAAE,OAAO,EAAE,KAAK,CAAA;KAAE,CACrB,CAAC;CACH;AAyDD,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,qBAAqB,CAAC,QAAQ,CAAC,EAC7C,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAyB7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CA0E5C;AA6ID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgCrD;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,4BAA4B,CAAC,CAgBvC;AA4vCD,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,sCAAsC,EAC/C,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAsEf"}
1
+ {"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"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"}
@@ -14,14 +14,13 @@ import { updateThreadData } from "../chat-threads/store.js";
14
14
  import { isLocalDatabase } from "../db/client.js";
15
15
  import { getOrgA2ASecret, resolveOrgIdForEmail } from "../org/context.js";
16
16
  import { withConfiguredAppBasePath } from "../server/app-base-path.js";
17
- import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
18
17
  import { canUseDeployCredentialFallbackForRequest, readDeployCredentialEnv, } from "../server/credential-provider.js";
19
18
  import { runWithRequestContext } from "../server/request-context.js";
20
19
  import { normalizeReasoningEffortForRequest } from "../shared/reasoning-effort.js";
21
20
  import { A2A_CONTINUATION_QUEUED_MARKER } from "./a2a-continuation-marker.js";
22
21
  import { clearIntegrationAwaitingInput, setIntegrationAwaitingInput, } from "./awaiting-input-store.js";
22
+ import { dispatchPendingIntegrationTask, integrationDispatchScopeValue, } from "./integration-durable-dispatch.js";
23
23
  import { loadIntegrationMemoryPrompt } from "./integration-memory.js";
24
- import { signInternalToken } from "./internal-token.js";
25
24
  import { insertPendingTask, isDuplicateEventError, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
26
25
  import { integrationScopeSubjectKey } from "./scope-store.js";
27
26
  import { getThreadMapping, saveThreadMapping } from "./thread-mapping-store.js";
@@ -138,7 +137,7 @@ export async function resolveIntegrationApiKey(engineOption, ownerEmail, fallbac
138
137
  * 2. Verify webhook signature
139
138
  * 3. Parse incoming message (null = ignored event)
140
139
  * 4. Persist task to SQL
141
- * 5. Fire-and-forget POST to /_agent-native/integrations/process-task
140
+ * 5. Dispatch the queued task through the configured processor handoff
142
141
  * (a fresh function execution with its own timeout budget)
143
142
  * 6. Return HTTP 200 immediately (within Slack's 3s SLA)
144
143
  *
@@ -280,50 +279,27 @@ async function enqueueAndDispatch(event, incoming, options, handlerStartedAt = D
280
279
  // platform produce the same key, so the unique index rejects the
281
280
  // second insert (H3 in the webhook security audit).
282
281
  externalEventKey: buildEventDedupKey(incoming),
282
+ dispatchScope: integrationDispatchScopeValue({
283
+ platform: incoming.platform,
284
+ externalThreadId: incoming.externalThreadId,
285
+ platformContext: incoming.platformContext,
286
+ }),
283
287
  });
284
288
  const baseUrl = resolveBaseUrl(event);
285
- const processUrl = `${baseUrl}${FRAMEWORK_ROUTE_PREFIX}/integrations/process-task`;
286
- // Sign the dispatch with an HMAC token so the processor endpoint can
287
- // verify the request came from us and not the public internet. The
288
- // processor refuses unsigned requests in production (C3 in the webhook
289
- // security audit). In dev, dispatching unsigned is allowed and falls
290
- // through to the SQL atomic claim for double-processing protection.
291
- const headers = {
292
- "Content-Type": "application/json",
293
- };
294
- try {
295
- headers["Authorization"] = `Bearer ${signInternalToken(taskId)}`;
296
- }
297
- catch (err) {
298
- // Distinguish "secret not configured" (the documented dev path) from
299
- // a real signing failure — silently swallowing both made malformed
300
- // secrets fail invisibly (L5 in the audit).
301
- if (err instanceof Error && !/A2A_SECRET/i.test(err.message)) {
302
- console.error(`[integrations] signInternalToken failed unexpectedly for ${taskId}:`, err);
303
- }
304
- }
305
- // Fire-and-forget: do NOT await the full response (the processor's run
306
- // takes minutes — we don't want to block the caller). BUT on Netlify
307
- // Lambda, when we return immediately, the runtime can freeze the function
308
- // before the outbound TCP handshake even starts, which leaves the dispatch
309
- // request stuck waiting for the 60s retry-sweep job. Race the fetch
310
- // against a short timer so the request gets a reasonable chance to leave
311
- // the box; the trade-off is at most a couple seconds of added webhook
312
- // latency, still inside Slack's timeout window.
313
- const dispatchPromise = fetch(processUrl, {
314
- method: "POST",
315
- headers,
316
- body: JSON.stringify({ taskId }),
317
- }).catch((err) => {
318
- console.error("[integrations] Failed to dispatch processor request:", err);
319
- });
320
289
  const settleWaitMs = options.adapter.capabilities?.deferredWebhookResponse
321
290
  ? Math.min(DEFERRED_RESPONSE_DISPATCH_SETTLE_WAIT_MS, Math.max(0, DEFERRED_RESPONSE_MAX_HANDLER_MS - (Date.now() - handlerStartedAt)))
322
291
  : PROCESSOR_DISPATCH_SETTLE_WAIT_MS;
323
- await Promise.race([
324
- dispatchPromise,
325
- new Promise((resolve) => setTimeout(resolve, settleWaitMs)),
326
- ]);
292
+ await dispatchPendingIntegrationTask({
293
+ taskId,
294
+ task: {
295
+ platform: incoming.platform,
296
+ externalThreadId: incoming.externalThreadId,
297
+ platformContext: incoming.platformContext,
298
+ },
299
+ event,
300
+ baseUrl,
301
+ portableSettleMs: settleWaitMs,
302
+ });
327
303
  }
328
304
  /**
329
305
  * Resolve the base URL we should dispatch the processor request to.