jat-feedback 2.0.1 → 3.0.1
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/README.md +176 -25
- package/dist/jat-feedback.js +40 -37
- package/dist/jat-feedback.mjs +3073 -2919
- package/package.json +3 -2
- package/routes/README.md +45 -0
- package/routes/tasks/+page.server.ts +55 -0
- package/routes/tasks/+page.svelte +698 -0
- package/supabase/functions/jat-webhook/index.ts +53 -22
- package/supabase/migrations/3.0.0_rename_to_project_tasks.sql +170 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Reports are stored in your Supabase database. The JAT ingest daemon polls for ne
|
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
User clicks "Report Bug" → Widget captures context → POST /api/feedback/report
|
|
9
|
-
→ Supabase
|
|
9
|
+
→ Supabase project_tasks table → JAT ingest daemon → JAT task created
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
## Install
|
|
@@ -128,6 +128,138 @@ ANTHROPIC_API_KEY=sk-ant-...
|
|
|
128
128
|
|
|
129
129
|
The Agent tab appears automatically when `agent-proxy` is set. Without it, only the feedback form and history tabs are shown.
|
|
130
130
|
|
|
131
|
+
### Page-Level Tool Registration
|
|
132
|
+
|
|
133
|
+
Register custom tools that the agent can call during its execution loop. Tools run client-side with full access to your app's state, auth context, and data — the LLM decides when to call them and reasons over the results.
|
|
134
|
+
|
|
135
|
+
#### `registerTools()` API Reference
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
interface ToolDefinition {
|
|
139
|
+
name: string;
|
|
140
|
+
description: string;
|
|
141
|
+
parameters: Record<string, unknown>; // JSON Schema object
|
|
142
|
+
handler: (args: Record<string, unknown>) => Promise<unknown>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const widget = document.querySelector('jat-feedback');
|
|
146
|
+
widget.registerTools(tools: ToolDefinition[]): void
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
| Field | Type | Description |
|
|
150
|
+
|-------|------|-------------|
|
|
151
|
+
| `name` | `string` | Unique tool name (snake_case). The LLM uses this to call the tool. |
|
|
152
|
+
| `description` | `string` | Tells the LLM what the tool does and when to use it. |
|
|
153
|
+
| `parameters` | `object` | JSON Schema describing the tool's arguments. Use `{ type: 'object', properties: {} }` for no-arg tools. |
|
|
154
|
+
| `handler` | `async (args) => any` | Runs client-side when the LLM calls the tool. Receives parsed args, returns any JSON-serializable value. |
|
|
155
|
+
|
|
156
|
+
**Behavior:**
|
|
157
|
+
- Multiple `registerTools()` calls **accumulate** — tools are added, not replaced
|
|
158
|
+
- Register tools before the user opens the Agent tab (typically in `onMount`)
|
|
159
|
+
- If a handler throws, the error message is returned to the LLM so it can recover gracefully
|
|
160
|
+
- Handlers have full access to the page's DOM, stores, and JS context
|
|
161
|
+
|
|
162
|
+
#### Example: Global Tools in SvelteKit
|
|
163
|
+
|
|
164
|
+
Register tools in your root `+layout.svelte` so they're available on every page. For page-specific tools, call `registerTools()` again in that page's layout or component — tools accumulate across calls.
|
|
165
|
+
|
|
166
|
+
```svelte
|
|
167
|
+
<script lang="ts">
|
|
168
|
+
import { page } from "$app/stores"
|
|
169
|
+
import { onMount } from "svelte"
|
|
170
|
+
|
|
171
|
+
onMount(() => {
|
|
172
|
+
const widget = document.querySelector("jat-feedback")
|
|
173
|
+
if (!widget?.registerTools) return
|
|
174
|
+
|
|
175
|
+
widget.registerTools([
|
|
176
|
+
{
|
|
177
|
+
name: "get_current_user",
|
|
178
|
+
description: "Get the currently authenticated user profile",
|
|
179
|
+
parameters: { type: "object", properties: {} },
|
|
180
|
+
handler: async () => {
|
|
181
|
+
const session = $page.data.session
|
|
182
|
+
if (!session?.user) return { authenticated: false }
|
|
183
|
+
const u = session.user
|
|
184
|
+
return {
|
|
185
|
+
authenticated: true,
|
|
186
|
+
id: u.id,
|
|
187
|
+
email: u.email,
|
|
188
|
+
name: u.user_metadata?.full_name ?? null,
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: "get_current_route",
|
|
194
|
+
description: "Get the current page URL, pathname, and route parameters",
|
|
195
|
+
parameters: { type: "object", properties: {} },
|
|
196
|
+
handler: async () => ({
|
|
197
|
+
pathname: $page.url.pathname,
|
|
198
|
+
params: $page.params,
|
|
199
|
+
search: Object.fromEntries($page.url.searchParams),
|
|
200
|
+
}),
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "get_page_data",
|
|
204
|
+
description: "Get data exposed by the current page's load function",
|
|
205
|
+
parameters: { type: "object", properties: {} },
|
|
206
|
+
handler: async () => {
|
|
207
|
+
const { supabase, session, ...rest } = $page.data
|
|
208
|
+
return rest
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
])
|
|
212
|
+
})
|
|
213
|
+
</script>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Example: Page-Specific Tools
|
|
217
|
+
|
|
218
|
+
Add tools that only make sense on a specific page:
|
|
219
|
+
|
|
220
|
+
```svelte
|
|
221
|
+
<!-- src/routes/admin/reports/+page.svelte -->
|
|
222
|
+
<script lang="ts">
|
|
223
|
+
import { onMount } from "svelte"
|
|
224
|
+
|
|
225
|
+
onMount(() => {
|
|
226
|
+
const widget = document.querySelector("jat-feedback")
|
|
227
|
+
if (!widget?.registerTools) return
|
|
228
|
+
|
|
229
|
+
widget.registerTools([
|
|
230
|
+
{
|
|
231
|
+
name: "update_report_status",
|
|
232
|
+
description: "Update a feedback report status",
|
|
233
|
+
parameters: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {
|
|
236
|
+
report_id: { type: "string" },
|
|
237
|
+
status: { type: "string", enum: ["open", "in_progress", "resolved"] },
|
|
238
|
+
},
|
|
239
|
+
required: ["report_id", "status"],
|
|
240
|
+
},
|
|
241
|
+
handler: async (args) => {
|
|
242
|
+
const { error } = await supabase
|
|
243
|
+
.from("project_tasks")
|
|
244
|
+
.update({ status: args.status })
|
|
245
|
+
.eq("id", args.report_id)
|
|
246
|
+
if (error) throw new Error(error.message)
|
|
247
|
+
return { success: true }
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
])
|
|
251
|
+
})
|
|
252
|
+
</script>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### How Tools Work
|
|
256
|
+
|
|
257
|
+
Tools are integrated into the page-agent's action loop:
|
|
258
|
+
- The LLM sees registered tools alongside built-in browser actions (click, type, scroll, etc.)
|
|
259
|
+
- Each agent step picks one action — either a browser action or a registered tool
|
|
260
|
+
- Tool results feed back to the LLM on the next step automatically
|
|
261
|
+
- If a handler throws, the error message is returned to the LLM so it can recover gracefully
|
|
262
|
+
|
|
131
263
|
### Error Handling
|
|
132
264
|
|
|
133
265
|
The widget handles proxy errors gracefully:
|
|
@@ -438,7 +570,7 @@ export const POST: RequestHandler = async ({ request, locals }) => {
|
|
|
438
570
|
const reporter = body.metadata?.reporter || {};
|
|
439
571
|
|
|
440
572
|
const { data: row, error: insertError } = await supabase
|
|
441
|
-
.from('
|
|
573
|
+
.from('project_tasks')
|
|
442
574
|
.insert({
|
|
443
575
|
title: body.title.trim(),
|
|
444
576
|
description: body.description?.trim() || '',
|
|
@@ -479,18 +611,21 @@ export const POST: RequestHandler = async ({ request, locals }) => {
|
|
|
479
611
|
|
|
480
612
|
### Step 3: Run the Supabase Migration
|
|
481
613
|
|
|
482
|
-
The `
|
|
614
|
+
The `project_tasks` table schema is included in this package. Copy it into your migrations folder and push:
|
|
483
615
|
|
|
484
616
|
```bash
|
|
485
|
-
# Copy
|
|
486
|
-
|
|
487
|
-
|
|
617
|
+
# Copy ALL migrations (rename to match your timestamp convention)
|
|
618
|
+
for f in node_modules/jat-feedback/supabase/migrations/*.sql; do
|
|
619
|
+
base=$(basename "$f" .sql)
|
|
620
|
+
cp "$f" "supabase/migrations/$(date +%Y%m%d%H%M%S)_feedback_${base}.sql"
|
|
621
|
+
sleep 1 # ensure unique timestamps
|
|
622
|
+
done
|
|
488
623
|
|
|
489
624
|
# Push to Supabase
|
|
490
625
|
supabase db push
|
|
491
626
|
```
|
|
492
627
|
|
|
493
|
-
**When upgrading jat-feedback:** check `node_modules/jat-feedback/supabase/migrations/` for new versioned files
|
|
628
|
+
**When upgrading jat-feedback:** check `node_modules/jat-feedback/supabase/migrations/` for new versioned files and copy+apply any you haven't run yet. The `3.0.0_rename_to_project_tasks.sql` migration renames `feedback_reports` to `project_tasks`.
|
|
494
629
|
|
|
495
630
|
### Step 4: Wire User Context
|
|
496
631
|
|
|
@@ -555,7 +690,7 @@ Add this entry to the `sources` array.
|
|
|
555
690
|
"priority": 2,
|
|
556
691
|
"labels": ["widget", "feedback"]
|
|
557
692
|
},
|
|
558
|
-
"table": "
|
|
693
|
+
"table": "project_tasks",
|
|
559
694
|
"statusColumn": "status",
|
|
560
695
|
"statusNew": "submitted",
|
|
561
696
|
"taskIdColumn": "jat_task_id",
|
|
@@ -586,7 +721,7 @@ Add this entry to the `sources` array.
|
|
|
586
721
|
},
|
|
587
722
|
"projectUrl": "https://YOUR_SUPABASE_PROJECT_ID.supabase.co",
|
|
588
723
|
"secretName": "YOUR_PROJECT-supabase-service-role",
|
|
589
|
-
"table": "
|
|
724
|
+
"table": "project_tasks",
|
|
590
725
|
"statusColumn": "status",
|
|
591
726
|
"statusNew": "submitted",
|
|
592
727
|
"taskIdColumn": "jat_task_id",
|
|
@@ -617,7 +752,7 @@ Add this entry to the `sources` array.
|
|
|
617
752
|
"in_progress": "in_progress",
|
|
618
753
|
"closed": "completed"
|
|
619
754
|
},
|
|
620
|
-
"referenceTable": "
|
|
755
|
+
"referenceTable": "project_tasks",
|
|
621
756
|
"referenceIdFrom": "item_id"
|
|
622
757
|
},
|
|
623
758
|
"actions": [
|
|
@@ -634,7 +769,7 @@ Add this entry to the `sources` array.
|
|
|
634
769
|
"label": "View in Supabase",
|
|
635
770
|
"description": "Open the original feedback report in Supabase dashboard",
|
|
636
771
|
"type": "link",
|
|
637
|
-
"urlTemplate": "{projectUrl}/project/default/editor/
|
|
772
|
+
"urlTemplate": "{projectUrl}/project/default/editor/project_tasks?filter=id%3Deq.{referenceId}",
|
|
638
773
|
"icon": "external-link"
|
|
639
774
|
}
|
|
640
775
|
]
|
|
@@ -649,7 +784,7 @@ The ingest daemon picks up config changes automatically (no restart needed).
|
|
|
649
784
|
|
|
650
785
|
### Step 6: Deploy the JAT Webhook Edge Function (for callbacks)
|
|
651
786
|
|
|
652
|
-
The `jat-webhook` Supabase Edge Function receives status-change callbacks from JAT and updates your `
|
|
787
|
+
The `jat-webhook` Supabase Edge Function receives status-change callbacks from JAT and updates your `project_tasks` rows. It's included in this package — copy it into your project and deploy it.
|
|
653
788
|
|
|
654
789
|
```bash
|
|
655
790
|
# Copy the function into your project
|
|
@@ -657,11 +792,19 @@ mkdir -p supabase/functions/jat-webhook
|
|
|
657
792
|
cp node_modules/jat-feedback/supabase/functions/jat-webhook/index.ts \
|
|
658
793
|
supabase/functions/jat-webhook/index.ts
|
|
659
794
|
|
|
660
|
-
# Deploy to Supabase
|
|
661
|
-
supabase functions deploy jat-webhook
|
|
795
|
+
# Deploy to Supabase (--no-verify-jwt required for service role key auth)
|
|
796
|
+
supabase functions deploy jat-webhook --no-verify-jwt
|
|
662
797
|
```
|
|
663
798
|
|
|
664
|
-
The function uses `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` — both are injected automatically by Supabase
|
|
799
|
+
The function uses `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` — both are injected automatically by Supabase.
|
|
800
|
+
|
|
801
|
+
**If callbacks fail with "Invalid authorization":** Newer Supabase projects use an `sb_secret_` format for the runtime `SUPABASE_SERVICE_ROLE_KEY`, which doesn't match the JWT service role key that JAT sends. Set a custom secret:
|
|
802
|
+
|
|
803
|
+
```bash
|
|
804
|
+
supabase secrets set JAT_WEBHOOK_SECRET="eyJhbG..." # paste your JWT service role key from API settings
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
The function checks `JAT_WEBHOOK_SECRET` first, falling back to `SUPABASE_SERVICE_ROLE_KEY`.
|
|
665
808
|
|
|
666
809
|
**Skip this step** if you don't need bidirectional status sync (i.e., you only want JAT to ingest reports, not push status back to Supabase).
|
|
667
810
|
|
|
@@ -677,7 +820,7 @@ jat ingest status
|
|
|
677
820
|
# → Should list your project's feedback source
|
|
678
821
|
|
|
679
822
|
# Submit a test report via the widget, then check:
|
|
680
|
-
# 1. Row appears in
|
|
823
|
+
# 1. Row appears in project_tasks table
|
|
681
824
|
# 2. JAT task gets created after next poll cycle (pollInterval seconds)
|
|
682
825
|
```
|
|
683
826
|
|
|
@@ -702,14 +845,22 @@ jat ingest status
|
|
|
702
845
|
|
|
703
846
|
## Column Reference
|
|
704
847
|
|
|
705
|
-
The `
|
|
706
|
-
|
|
707
|
-
| Column | Type | Purpose |
|
|
708
|
-
|
|
709
|
-
| `status` | TEXT | Lifecycle: `submitted` → `in_progress` → `completed` → `accepted` \| `rejected` |
|
|
710
|
-
| `jat_task_id` | TEXT | JAT task ID written back after ingest (e.g., `myapp-abc`) |
|
|
711
|
-
| `rejection_reason` | TEXT | User-provided reason when rejecting a completed report |
|
|
712
|
-
| `dev_notes` | TEXT | Developer notes pushed back via callback |
|
|
848
|
+
The `project_tasks` table columns used by the pipeline:
|
|
849
|
+
|
|
850
|
+
| Column | Type | Since | Purpose |
|
|
851
|
+
|--------|------|-------|---------|
|
|
852
|
+
| `status` | TEXT | 1.0.0 | Lifecycle: `submitted` → `in_progress` → `completed` → `accepted` \| `rejected` |
|
|
853
|
+
| `jat_task_id` | TEXT | 1.0.0 | JAT task ID written back after ingest (e.g., `myapp-abc`) |
|
|
854
|
+
| `rejection_reason` | TEXT | 1.0.0 | User-provided reason when rejecting a completed report |
|
|
855
|
+
| `dev_notes` | TEXT | 1.0.0 | Developer notes pushed back via callback |
|
|
856
|
+
| `source_type` | TEXT | 3.0.0 | Original `type` column renamed — `bug`, `enhancement`, `other` |
|
|
857
|
+
| `source` | TEXT | 3.0.0 | Where the item came from: `feedback`, `jat`, `manual` |
|
|
858
|
+
| `issue_type` | TEXT | 3.0.0 | Classification: `bug`, `feature`, `task`, `epic` |
|
|
859
|
+
| `assignee` | TEXT | 3.0.0 | Assigned agent or person |
|
|
860
|
+
| `due_date` | TIMESTAMPTZ | 3.0.0 | Due date for the task |
|
|
861
|
+
| `labels` | TEXT[] | 3.0.0 | Flexible categorization labels |
|
|
862
|
+
| `parent_id` | UUID | 3.0.0 | Self-referential parent for hierarchical tasks |
|
|
863
|
+
| `updated_at` | TIMESTAMPTZ | 3.0.0 | Auto-updated timestamp |
|
|
713
864
|
|
|
714
865
|
## Upgrading
|
|
715
866
|
|
|
@@ -746,7 +897,7 @@ supabase db push
|
|
|
746
897
|
cp node_modules/jat-feedback/supabase/functions/jat-webhook/index.ts \
|
|
747
898
|
supabase/functions/jat-webhook/index.ts
|
|
748
899
|
|
|
749
|
-
supabase functions deploy jat-webhook
|
|
900
|
+
supabase functions deploy jat-webhook --no-verify-jwt
|
|
750
901
|
```
|
|
751
902
|
|
|
752
903
|
## Versioning
|