From d039a8ba9538126019fc6dc95661ee4179a55e86 Mon Sep 17 00:00:00 2001 From: pikann Date: Tue, 12 May 2026 10:03:45 +0700 Subject: [PATCH 1/3] feat: implement RecordActivity function and update CallerIdentity struct --- native_backends.go | 3 +++ request.go | 3 +++ wasm_backends.go | 27 +++++++++++++++++++++++++++ wasm_imports.go | 6 ++++++ 4 files changed, 39 insertions(+) diff --git a/native_backends.go b/native_backends.go index 347d069..16a0bf3 100644 --- a/native_backends.go +++ b/native_backends.go @@ -45,6 +45,9 @@ func (b *stubConfigBackend) Get(_ string) (string, bool) { return "", false } // EmitEvent is a no-op outside WASM. func EmitEvent(_ string, _ any) {} +// RecordActivity is a no-op outside WASM. +func RecordActivity(_, _, _, _ string, _ any) {} + // ptrOf and hostError are used by wasm_backends.go (wasip1 only); provide // stubs here so the non-WASM build does not need them. //nolint:unused // used in wasm_backends.go in WASM builds diff --git a/request.go b/request.go index b8a982e..da03e3b 100644 --- a/request.go +++ b/request.go @@ -7,6 +7,9 @@ import "encoding/json" type CallerIdentity struct { // CallerID is the project_member UUID of the caller. CallerID string `json:"caller_id"` + // UserID is the authenticated user's UUID (JWT sub claim). + // Use this as actor_id when recording task activities. + UserID string `json:"user_id"` // CallerRole is the role name of the caller within the project. CallerRole string `json:"caller_role"` // ProjectID is the project the request is scoped to. diff --git a/wasm_backends.go b/wasm_backends.go index 9d77624..7fc01e3 100644 --- a/wasm_backends.go +++ b/wasm_backends.go @@ -179,6 +179,33 @@ func EmitEvent(topic string, payload any) { ) } +// ── RecordActivity ──────────────────────────────────────────────────────────── + +// activityInput is the JSON shape sent to the paca.activity_record host function. +type activityInput struct { + TaskID string `json:"task_id"` + ProjectID string `json:"project_id"` + ActorID string `json:"actor_id,omitempty"` + ActivityType string `json:"activity_type"` + Content any `json:"content"` +} + +// RecordActivity appends a task-activity event to the paca activity stream so +// that it is persisted to PostgreSQL by the ActivityConsumer worker. +// actorUserID should be req.Caller.UserID (the authenticated user's UUID). +// content must be JSON-encodable and match the expected shape for activityType. +func RecordActivity(taskID, projectID, actorUserID, activityType string, content any) { + inp := activityInput{ + TaskID: taskID, + ProjectID: projectID, + ActorID: actorUserID, + ActivityType: activityType, + Content: content, + } + payloadBytes, _ := json.Marshal(inp) + hostActivityRecord(int64(ptrOf(payloadBytes)), int64(len(payloadBytes))) +} + // ── Helpers ─────────────────────────────────────────────────────────────────── //go:nocheckptr diff --git a/wasm_imports.go b/wasm_imports.go index 3cb4243..8393a94 100644 --- a/wasm_imports.go +++ b/wasm_imports.go @@ -49,6 +49,12 @@ func hostStorageDelete(keyPtr, keyLen int64) int32 //go:noescape func hostEventEmit(topicPtr, topicLen, payloadPtr, payloadLen int64) int32 +// paca.activity_record(payloadPtr i64, payloadLen i64) -> ok i32 +// +//go:wasmimport paca activity_record +//go:noescape +func hostActivityRecord(payloadPtr, payloadLen int64) int32 + // paca.config_get(keyPtr i64, keyLen i64, valuePtrPtr i64, valueLenPtr i64) // //go:wasmimport paca config_get From c2131f2ede2914735d7cf43e2d2468039ce1a775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=E1=BA=A3i=20Hu=E1=BB=B3nh?= <41873019+pikann@users.noreply.github.com> Date: Tue, 12 May 2026 10:11:27 +0700 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- wasm_backends.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wasm_backends.go b/wasm_backends.go index 7fc01e3..f58afe2 100644 --- a/wasm_backends.go +++ b/wasm_backends.go @@ -202,8 +202,13 @@ func RecordActivity(taskID, projectID, actorUserID, activityType string, content ActivityType: activityType, Content: content, } - payloadBytes, _ := json.Marshal(inp) - hostActivityRecord(int64(ptrOf(payloadBytes)), int64(len(payloadBytes))) + payloadBytes, err := json.Marshal(inp) + if err != nil { + return + } + if !hostActivityRecord(int64(ptrOf(payloadBytes)), int64(len(payloadBytes))) { + return + } } // ── Helpers ─────────────────────────────────────────────────────────────────── From be1e52261b768b261c38d8ca96cd0927709d86b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 03:13:03 +0000 Subject: [PATCH 3/3] fix: populate UserID in dispatch.go and remove omitempty from actor_id Agent-Logs-Url: https://github.com/Paca-AI/plugin-sdk-go/sessions/244e47a5-a8a9-4aff-bedf-a6e4d5fcab1b Co-authored-by: pikann <41873019+pikann@users.noreply.github.com> --- dispatch.go | 2 ++ wasm_backends.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dispatch.go b/dispatch.go index 87fe01b..5f17695 100644 --- a/dispatch.go +++ b/dispatch.go @@ -45,6 +45,7 @@ func (d *dispatcher) handleRequest(payload []byte) []byte { Query map[string]string `json:"query"` ProjectID string `json:"project_id"` CallerID string `json:"caller_id"` + UserID string `json:"user_id"` CallerRole string `json:"caller_role"` Headers map[string]string `json:"headers"` Body []byte `json:"body"` @@ -70,6 +71,7 @@ func (d *dispatcher) handleRequest(payload []byte) []byte { Body: hr.Body, Caller: CallerIdentity{ CallerID: hr.CallerID, + UserID: hr.UserID, CallerRole: hr.CallerRole, ProjectID: hr.ProjectID, }, diff --git a/wasm_backends.go b/wasm_backends.go index f58afe2..8e6e2eb 100644 --- a/wasm_backends.go +++ b/wasm_backends.go @@ -185,7 +185,7 @@ func EmitEvent(topic string, payload any) { type activityInput struct { TaskID string `json:"task_id"` ProjectID string `json:"project_id"` - ActorID string `json:"actor_id,omitempty"` + ActorID string `json:"actor_id"` ActivityType string `json:"activity_type"` Content any `json:"content"` }