@agent-native/core 0.210.1 → 0.211.0-nightly-20261020183616
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/action.d.ts +8 -0
- package/dist/action.js +17 -0
- package/dist/agent/production-agent.js +11 -20
- package/dist/client/chat/agentkit-protocol.js +48 -5
- package/dist/client/chat/runtime.d.ts +2 -0
- package/dist/client/chat/runtime.js +6 -1
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/resources/mcp-integration-logos.js +4 -0
- package/dist/client/session-identity.d.ts +8 -0
- package/dist/client/session-identity.js +13 -0
- package/dist/client/use-action.d.ts +14 -0
- package/dist/client/use-action.js +117 -28
- package/dist/client/use-agent-engine-configured.d.ts +2 -2
- package/dist/client/use-agent-engine-configured.js +3 -9
- package/dist/client/use-session.js +2 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +24 -3
- package/dist/mcp/actions/revoke-org-service-token.js +5 -1
- package/dist/mcp/execute-action-call.js +8 -17
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/actions/set-app-member-roles.js +4 -0
- package/dist/org/actions/set-app-permission-roles.js +4 -0
- package/dist/org/actions/set-workspace-app-access.js +4 -0
- package/dist/org/index.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +17 -17
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/review/suggestions/actions.d.ts +1 -0
- package/dist/review/suggestions/actions.js +54 -42
- package/dist/secrets/actions/delete-api-key.js +4 -0
- package/dist/secrets/routes.d.ts +4 -4
- package/dist/server/action-approval.d.ts +15 -0
- package/dist/server/action-approval.js +39 -0
- package/dist/server/action-routes.js +33 -28
- package/dist/server/edge.d.ts +1 -0
- package/dist/server/edge.js +1 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.js +1 -0
- package/dist/shared/action-error-codes.d.ts +5 -0
- package/dist/shared/action-error-codes.js +5 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/templates/default/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/vite/client.js +2 -1
- package/docs/content/actions-access-control.mdx +3 -3
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/src/templates/default/.agents/skills/actions/references/action-fields.md +5 -3
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/src/templates/headless/.agents/skills/actions/references/action-fields.md +5 -3
- package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +5 -3
package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md
CHANGED
|
@@ -3,15 +3,27 @@
|
|
|
3
3
|
Use this procedure before investigating, claiming, or using a CI rollup in
|
|
4
4
|
place of Slack source messages.
|
|
5
5
|
|
|
6
|
+
At the start of a sweep, capture one Slack timestamp as `windowEndTs`. Use it
|
|
7
|
+
as the inclusive `before` argument on the source scan and all three `hasmy`
|
|
8
|
+
ownership searches in every scoped channel. Use the same inclusive `after` on
|
|
9
|
+
the source scan and ownership searches. Record the bounds in `scanQuery` and
|
|
10
|
+
each owned search's `query`. Keep `after` fixed through the sweep. Keep the
|
|
11
|
+
exact upper bound for the whole sweep; page-cap recovery slices may narrow only
|
|
12
|
+
`before` using the existing one-microsecond boundary rule. Messages newer than
|
|
13
|
+
`windowEndTs` belong to the next scheduled sweep. Never advance either bound to
|
|
14
|
+
chase arrivals or expand a finite catch-up request into an all-history audit.
|
|
15
|
+
|
|
6
16
|
- For the exhaustive channel source scan, use `slack_search_public` with only
|
|
7
|
-
`filters=in:<#CHANNEL>`, `after=<channel cutoff>`,
|
|
17
|
+
`filters=in:<#CHANNEL>`, `after=<channel cutoff>`,
|
|
18
|
+
`before=<windowEndTs>`, `keywords=[]`, an empty
|
|
8
19
|
`natural_language_query`, `include_bots=true`, `include_context=false`,
|
|
9
20
|
`content_types=messages`, `limit=20`, `sort=timestamp`, `sort_dir=desc`, and
|
|
10
21
|
`response_format=detailed`. Do not add lexical, semantic, date, author, or
|
|
11
22
|
reaction filters: they silently hide reports. Record these exact arguments
|
|
12
23
|
under that channel's `scanQuery`; the inventory checker rejects a missing or
|
|
13
|
-
narrowed query. `after`
|
|
14
|
-
cutoff
|
|
24
|
+
narrowed query. `after` and `before` are inclusive and must equal that
|
|
25
|
+
channel's effective lower cutoff and the sweep's fixed upper bound. Follow
|
|
26
|
+
every `next_cursor` newest-first to Slack exhaustion, even
|
|
15
27
|
after reaching the cutoff, because later pages can contain replies to older
|
|
16
28
|
roots. Page and
|
|
17
29
|
source-row timestamps must descend across the full chain. This is separate
|
|
@@ -50,9 +62,18 @@ place of Slack source messages.
|
|
|
50
62
|
`ts >= effectiveCutoffTs`, and record the exact `oldest` in `oldestQuery`.
|
|
51
63
|
- Record `scope.channelIds` as the exact IDs requested and scanned. The default
|
|
52
64
|
sweep lists all three default feedback channels. For an explicitly narrowed
|
|
53
|
-
invocation, inspect the active user request itself
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
invocation, inspect the active user request itself. It can name the exact
|
|
66
|
+
channel set with an explicit limit such as “only” and exact IDs or
|
|
67
|
+
`#channel-name`s. It can also select exactly two channels by linking one
|
|
68
|
+
report permalink in the first channel and directly asking for “the same in
|
|
69
|
+
channel-name” (with or without `#`) in the second; that direct linked-report
|
|
70
|
+
form selects exactly those two channels and does not need an exclusivity
|
|
71
|
+
word. It requires exactly one linked root and one direct “same in” target. In
|
|
72
|
+
that clause only, accept a single adjacent transposition in the channel name
|
|
73
|
+
only when it uniquely resolves to one canonical channel in the requested
|
|
74
|
+
set. Do not infer scope from incidental or quoted channel mentions or a
|
|
75
|
+
channel-name prefix. A permalink or time anchor by itself does not narrow
|
|
76
|
+
channel scope. Ask the user if the
|
|
56
77
|
requested channels are unclear. Store the exact instruction in
|
|
57
78
|
`scope.userRequest` and `scope.narrowingJudgment`, then create a separate
|
|
58
79
|
`.tmp/feedback-scope-confirmation.json` from that active request with
|
|
@@ -85,10 +106,12 @@ place of Slack source messages.
|
|
|
85
106
|
`administrative` decision, and a human rationale tied to that text. This is
|
|
86
107
|
an attributed human triage judgment; the checker verifies its presence and
|
|
87
108
|
source-text linkage but cannot prove the interpretation is correct.
|
|
88
|
-
For every classified root, including administrative posts, capture a
|
|
109
|
+
For every unheld classified root, including administrative posts, capture a
|
|
89
110
|
complete thread read and make `sourceText` exactly match its `rootText`.
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
A held root keeps the exact channel-scan `sourceText` and direct reaction
|
|
112
|
+
evidence but has no thread-derived fields. Search output can confuse
|
|
113
|
+
user-authored `Files:` or `Reactions:` lines with connector metadata, so do
|
|
114
|
+
not classify from a truncated search excerpt.
|
|
92
115
|
For a canonical Slack message permalink, inspect its `thread_ts` query value:
|
|
93
116
|
if it differs from the message's `ts`, the message is a reply and that value
|
|
94
117
|
is its root. A self-valued `thread_ts` or a canonical root permalink without
|
|
@@ -194,9 +217,9 @@ place of Slack source messages.
|
|
|
194
217
|
root message.
|
|
195
218
|
Read and capture a complete, oldest-first `threadSnapshot` after the final
|
|
196
219
|
channel page for every in-scope classified root, including administrative
|
|
197
|
-
posts, unmarked roots, and roots with no replies
|
|
198
|
-
|
|
199
|
-
`sourceText`.
|
|
220
|
+
posts, unmarked roots, and roots with no replies, except roots held by the
|
|
221
|
+
reaction ownership boundary below. The checker rejects a classified root
|
|
222
|
+
without this read or with `rootText` that differs from its `sourceText`.
|
|
200
223
|
Supplemental roots must strictly predate the oldest `sourceTimestamps` entry
|
|
201
224
|
across every scanned page, as well as `cutoffTs`; a root inside the scanned
|
|
202
225
|
source range must have its own source-manifest row even when it predates the
|
|
@@ -205,8 +228,8 @@ place of Slack source messages.
|
|
|
205
228
|
`ts === threadParentTs` and must not be duplicated in `threadRoots`. A
|
|
206
229
|
supplemental root exists to resolve an in-window reply;
|
|
207
230
|
its presence does not make the older report a new in-scope claim. Read its
|
|
208
|
-
exact root reactions
|
|
209
|
-
|
|
231
|
+
exact root reactions, then apply the ownership gate before opening the thread.
|
|
232
|
+
Do not add `👀`, `✅`, or `🎫`, or post a status, just because
|
|
210
233
|
an unowned older root appears as context. If this actor already owns a mark
|
|
211
234
|
on that older root, reconcile its current status and scopes as usual; record
|
|
212
235
|
another actor's marks as ownership evidence and leave them untouched. Their
|
|
@@ -214,29 +237,53 @@ place of Slack source messages.
|
|
|
214
237
|
shows an open disposition; a later closed status makes that eye historical.
|
|
215
238
|
Keep
|
|
216
239
|
the root disposition and any open scope in the audit row without claiming it.
|
|
217
|
-
|
|
218
|
-
`threadSnapshot
|
|
219
|
-
|
|
220
|
-
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`
|
|
224
|
-
`
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
the
|
|
240
|
+
For an unheld supplemental root, the inventory checker still requires a
|
|
241
|
+
complete, oldest-first `threadSnapshot`, so a reply cannot predate or
|
|
242
|
+
otherwise contradict its reported root.
|
|
243
|
+
- **Held thread:** If the exact root reaction read shows another actor owns any
|
|
244
|
+
workflow mark (`👀`, `✅`, or `🎫`), or this actor owns `:mag:`, do not open or
|
|
245
|
+
read the thread, inspect reply reactions, react, or reply. Record an
|
|
246
|
+
`ownershipSkip` with the root timestamp and `foreign_workflow_mark` or
|
|
247
|
+
`mag_opt_out`; use `Skipped` and leave thread-derived fields empty. If this
|
|
248
|
+
actor also owns a workflow mark, use `mag_opt_out` and preserve the exact
|
|
249
|
+
direct root reaction-owner record as history. The checker treats it as held
|
|
250
|
+
and requires no thread snapshot or status. Mark every
|
|
251
|
+
channel reply linked to that root with the same skip reference. The checker
|
|
252
|
+
requires direct root reaction evidence and the matching skip reason, but no
|
|
253
|
+
full-thread snapshot. For a linked pre-window root without source text, use
|
|
254
|
+
`unclassified`; do not guess from its in-window reply. This boundary also
|
|
255
|
+
applies when the other actor's eye has no visible status reply: ownership is
|
|
256
|
+
not permission to inspect. The direct root-reaction read must still be fresh
|
|
257
|
+
and after the final source page; it is the only read allowed for a held root.
|
|
258
|
+
Preserve the source's exact file ID, name, and MIME type when its raw search
|
|
259
|
+
result includes `Files:`. If that result omits file metadata, leave the held
|
|
260
|
+
root's `files` field absent to represent unknown; do not record `[]`. For an
|
|
261
|
+
unheld root, use the full thread read to record files and require the source
|
|
262
|
+
row and snapshot to agree.
|
|
263
|
+
- After each bounded channel scan, run three separate bounded ownership
|
|
264
|
+
searches for this actor's `👀`, `✅`, and `🎫` with `slack_search_public`. Each
|
|
265
|
+
query uses only `in:<#CHANNEL> hasmy::EMOJI:` plus the same inclusive `after`
|
|
266
|
+
and `before` as the source scan. These searches cover only this invocation's
|
|
267
|
+
finite window; do not search unrelated channel history. An older root is in
|
|
268
|
+
scope only when an in-window reply links to it. Resolve that reply's true
|
|
269
|
+
parent, read the older root's exact reactions, and apply the held-thread gate
|
|
270
|
+
before opening it. Reconcile every candidate in the window and each linked
|
|
271
|
+
older root; unheld roots need exact reaction and full-thread evidence in the
|
|
272
|
+
source or supplemental-root audit. A candidate missing either audit is
|
|
273
|
+
unresolved, not outside scope.
|
|
274
|
+
If Slack caps a cursor before exhaustion, preserve that capped page and
|
|
275
|
+
record the exact `page_limit_exceeded` response in that search's
|
|
229
276
|
`pageLimitRecoveries[]`, including its failed cursor and request args, raw
|
|
230
277
|
response, read time, and the capped page's oldest result as `boundaryTs`.
|
|
231
|
-
Restart the same
|
|
232
|
-
`before` exactly one microsecond below that boundary. Keep
|
|
233
|
-
every page in the restarted cursor slice and in each raw request wrapper
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
Restart the same `hasmy` query at `cursor: null` with the same fixed `after`
|
|
279
|
+
and a `before` exactly one microsecond below that boundary. Keep both bounds
|
|
280
|
+
on every page in the restarted cursor slice and in each raw request wrapper.
|
|
281
|
+
Follow the restarted slice to exhaustion and repeat this recovery for every
|
|
282
|
+
later cap. The checker proves each failed cursor against exactly one
|
|
283
|
+
restarted slice, checks the boundary and read order, and requires explicit
|
|
284
|
+
exhaustion. If Slack does not let you recover a capped tail, report the
|
|
285
|
+
bounded ownership search as incomplete; do not claim every in-window owned
|
|
286
|
+
mark was reconciled.
|
|
240
287
|
Record each exact query and page in `ownedMarkSearches`; save raw wrappers
|
|
241
288
|
named `owned-marks-{channelName}-{eyes|white_check_mark|ticket}-00.json` and
|
|
242
289
|
continue numbering for each cursor page. Each wrapper preserves exact
|
|
@@ -252,16 +299,19 @@ place of Slack source messages.
|
|
|
252
299
|
evidence; if the exact read fails or is incomplete, leave ownership
|
|
253
300
|
unresolved rather than treating the query as confirmation. A result with
|
|
254
301
|
`ts !== root` is reply-level evidence: resolve and preserve its parent, but
|
|
255
|
-
never count it as a root checkoff.
|
|
256
|
-
reply read-back in its complete thread snapshot; if that
|
|
257
|
-
cannot be read back, report reply-level reconciliation as
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
302
|
+
never count it as a root checkoff. For an unheld root, verify reply reactions
|
|
303
|
+
from the exact reply read-back in its complete thread snapshot; if that
|
|
304
|
+
historical reply cannot be read back, report reply-level reconciliation as
|
|
305
|
+
incomplete. For a held root, preserve the reply parent and matching skip
|
|
306
|
+
reason without opening the thread.
|
|
307
|
+
Deduplicate confirmed roots across the three searches. Every in-window root
|
|
308
|
+
candidate must appear in the source pages. An older root linked by an
|
|
309
|
+
in-window reply belongs in `threadRoots` and `threadRootAudits`. If a bounded
|
|
310
|
+
search hit has no matching source row or linked supplemental root, rerun the
|
|
311
|
+
source scan and include the missing evidence; do not invent an audit row.
|
|
312
|
+
Reconcile each unheld discovered root's latest human message, status reply,
|
|
313
|
+
scopes, and exact current reaction state. Read its full thread after the final
|
|
314
|
+
ownership-search page and preserve every raw
|
|
265
315
|
`slack_read_thread` page under `thread-{channelName}-{rootTs}-00.json` and
|
|
266
316
|
contiguous page numbers. Each wrapper records exact request `args`,
|
|
267
317
|
requested `cursor`, raw response, and `readAt`; the raw response must
|
|
@@ -387,9 +437,13 @@ place of Slack source messages.
|
|
|
387
437
|
and every source `{ ts, root }` row with the reviewed inventory and raw Slack
|
|
388
438
|
responses. If it finds a mismatch,
|
|
389
439
|
rebuild the inventory from the captured Slack pages and rerun the check; never
|
|
390
|
-
edit the receipt to fit the inventory.
|
|
391
|
-
|
|
392
|
-
|
|
440
|
+
edit the receipt to fit the inventory. Source and ownership-search pages are
|
|
441
|
+
immutable evidence for the fixed `after`/`before` interval; their age does not
|
|
442
|
+
restart the sweep. The 15-minute freshness window applies to current root and
|
|
443
|
+
reply reaction reads and full-thread snapshots. If one expires, reread only
|
|
444
|
+
that exact state. After one targeted retry, if a required current-state read
|
|
445
|
+
is still stale or unavailable, mark the inventory incomplete and stop; do
|
|
446
|
+
not restart the channel-wide search or loop.
|
|
393
447
|
It rejects any root mark this actor owns without a current same-actor
|
|
394
448
|
status reply, a
|
|
395
449
|
assigned-human handoff without that person's explicit acceptance, an
|
|
@@ -434,19 +488,18 @@ verified disposition may retain the eye as history. Never remove or clean up a
|
|
|
434
488
|
reaction. Slack thread replies appear under the root's reply count, not as
|
|
435
489
|
separate channel posts; do not broadcast routine status replies.
|
|
436
490
|
|
|
437
|
-
After
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
before checking the inventory. Only a complete fresh pass can certify coverage.
|
|
491
|
+
After a long investigation, CI/deploy wait, or handoff, do one final exhaustive
|
|
492
|
+
pass of the exact original channel-wide query for every scoped channel, using
|
|
493
|
+
the same recorded `scanQuery.after` and `scanQuery.before`. Replace the saved
|
|
494
|
+
page responses, timestamps, source manifests, and source rows; never append a
|
|
495
|
+
delta or refresh `readAt` without rereading the page. This is one bounded
|
|
496
|
+
reconciliation pass, not a moving high-water mark. Resolve newly found replies
|
|
497
|
+
to their actual roots and reconcile them inside the fixed interval. Then reread
|
|
498
|
+
current root reactions and full threads for marked roots after the final source
|
|
499
|
+
and ownership-search pages. If an exact current-state read expires, reread that
|
|
500
|
+
target once. If it still cannot pass freshness, mark coverage incomplete and
|
|
501
|
+
stop; do not restart the global scan. Activity newer than `windowEndTs` is for
|
|
502
|
+
the next scheduled sweep.
|
|
450
503
|
|
|
451
504
|
Channel history does not include the contents of existing thread replies. In
|
|
452
505
|
that final pass, also enumerate every distinct root thread carrying this
|
|
@@ -492,21 +545,22 @@ read time as `reactionReadAt`; when this sweep writes a reaction, record
|
|
|
492
545
|
also requires each owned root reaction read and the full-thread reaction reads
|
|
493
546
|
to follow the final channel page.
|
|
494
547
|
|
|
495
|
-
The
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
548
|
+
The inventory checker requires source pages to use one recorded inclusive
|
|
549
|
+
`after`/`before` window and ownership-census pages to use the fixed `before`
|
|
550
|
+
upper bound with no `after`. Both page chains must remain in cursor order and
|
|
551
|
+
have valid local ISO read times. Their age does not invalidate those fixed
|
|
552
|
+
bounds. Current root and reply reaction reads and full-thread snapshots must be
|
|
553
|
+
within 15 minutes, allow at most one minute of local clock skew, and follow the
|
|
554
|
+
final source and ownership-search pages. Refresh only a stale current-state
|
|
555
|
+
target; if one targeted reread is still stale or unavailable, mark coverage
|
|
556
|
+
incomplete and stop instead of repeating the full sweep.
|
|
503
557
|
|
|
504
558
|
The inventory checker requires a complete `threadSnapshot` for every in-scope
|
|
505
|
-
classified root
|
|
506
|
-
|
|
507
|
-
raw thread page cursor exhaustion, Slack's reply count, exact source
|
|
508
|
-
attachments, and snapshot order, and rejects a newer human or sweep reply
|
|
509
|
-
the recorded status. A failed check means the sweep is not reconciled.
|
|
559
|
+
classified root and every supplemental root included to resolve an in-scope
|
|
560
|
+
reply, except roots recorded under the held-thread boundary. For unheld roots it
|
|
561
|
+
checks raw thread page cursor exhaustion, Slack's reply count, exact source
|
|
562
|
+
text, attachments, and snapshot order, and rejects a newer human or sweep reply
|
|
563
|
+
than the recorded status. A failed check means the sweep is not reconciled.
|
|
510
564
|
|
|
511
565
|
A grouped CI report or attachment is a discovery index, never a replacement for
|
|
512
566
|
the channel-message inventory.
|
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Use the connected Slack integration for every Slack read and write. Never use a browser, screenshots, or UI automation to operate Slack.
|
|
4
4
|
|
|
5
|
-
At sweep exit, reconcile every report root in the scoped inventory that
|
|
6
|
-
this task's `👀`, `✅`, or `🎫`, including marks left by earlier runs.
|
|
5
|
+
At sweep exit, reconcile every unheld report root in the scoped inventory that
|
|
6
|
+
has this task's `👀`, `✅`, or `🎫`, including marks left by earlier runs. A
|
|
7
|
+
root is held when a direct reaction read shows another actor owns any workflow
|
|
8
|
+
mark or this actor owns `:mag:`. For held roots, record the root and safe-skip
|
|
9
|
+
reason, then stop: do not open the thread, inspect reply reactions, write any
|
|
10
|
+
reaction, or post a status. When this actor owns both `:mag:` and a workflow
|
|
11
|
+
mark, use `mag_opt_out` and preserve the exact direct root reaction-owner
|
|
12
|
+
record as history; the root remains held without a thread read or status reply.
|
|
13
|
+
Capture
|
|
7
14
|
the exact Slack actor ID used by the connected tool at sweep start. A mark is
|
|
8
15
|
owned by this task only when its reaction owner ID matches that actor ID. Each
|
|
9
|
-
owned root needs a current disposition and a matching status reply by
|
|
10
|
-
actor under that same parent, even when the root has only `✅` or `🎫`. A
|
|
16
|
+
unheld owned root needs a current disposition and a matching status reply by
|
|
17
|
+
that same actor under that same parent, even when the root has only `✅` or `🎫`. A
|
|
11
18
|
same-run fix gets its final reply after verification; continuing work gets a
|
|
12
19
|
concrete progress reply. An earlier generic “taking a look” does not satisfy
|
|
13
20
|
this update. If an existing reply already names the exact canonical disposition
|
|
@@ -30,6 +37,23 @@ includes human handoffs, the status reply must name each handoff's recorded
|
|
|
30
37
|
owner and action; include its scope when the report context does not make it
|
|
31
38
|
clear.
|
|
32
39
|
|
|
40
|
+
Interpret the three root reactions independently from the current disposition:
|
|
41
|
+
|
|
42
|
+
- `👀` is a permanent claim/history mark, not an open/closed indicator.
|
|
43
|
+
- `✅` is permanent history for named scopes that passed direct post-change
|
|
44
|
+
proof. Add it as soon as any scope passes; do not wait for other scopes or
|
|
45
|
+
merge. Keep it if later evidence changes the scope's status, and explain the
|
|
46
|
+
historical mark in the current reply and inventory.
|
|
47
|
+
- `🎫` is permanent history for a distinct human action, including an
|
|
48
|
+
unassigned team action or reporter request. Keep it after completion,
|
|
49
|
+
answer, or four-day abandonment; label it historical in the current reply
|
|
50
|
+
and inventory when it no longer blocks progress.
|
|
51
|
+
|
|
52
|
+
The current status reply is the source for whether work remains open. When a
|
|
53
|
+
root has only `👀`, say that no scope has passed the proof bar for `✅`, whether
|
|
54
|
+
the next step belongs to this workflow, and why no separate human action needs
|
|
55
|
+
`🎫`. Reuse a current reply rather than posting again on every sweep.
|
|
56
|
+
|
|
33
57
|
An evidence-only addendum or a correction is not a current status. When your
|
|
34
58
|
newest own reply is one of those, edit that reply into the full canonical
|
|
35
59
|
disposition when possible; otherwise send one final status after the latest
|
|
@@ -101,10 +125,13 @@ slack_search: "this was sent from a bot." in:<#CHANNEL>
|
|
|
101
125
|
sort=timestamp sort_dir=asc include_context=true max_context_length=300
|
|
102
126
|
```
|
|
103
127
|
|
|
104
|
-
Also scan channel messages from the saved reply cursor
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
Also scan channel messages from the saved reply cursor within the current
|
|
129
|
+
invocation's fixed window; on the first run, use the default five-day window,
|
|
130
|
+
not all available history. Since `after:` is date-only, start one day before
|
|
131
|
+
the later of the saved cursor and window start, exhaust pages, resolve hits to
|
|
132
|
+
their root, and advance the cursor only through processed messages. An older
|
|
133
|
+
root is eligible only when an in-window reply links to it. Read inherited
|
|
134
|
+
threads carrying any of this actor's root marks (`👀`, `✅`,
|
|
108
135
|
or `🎫`): a checkmark closes only its verified scope, not a newer reporter
|
|
109
136
|
answer or another open scope. Do not reply or react on another actor's root when work is handed off for investigation; that handoff permits local investigation only. A separate explicit authorization is required to post a status or reporter reply there.
|
|
110
137
|
|
|
@@ -130,9 +157,10 @@ human/team actions, including actions with no named owner yet. The eye alone
|
|
|
130
157
|
does not mean work remains open. One root `✅` covers every verified scope
|
|
131
158
|
listed in `fixedScopes[]`; one root `🎫` covers every action listed in
|
|
132
159
|
`handoffs[]`, including unassigned, accepted, and completed actions. Completion
|
|
133
|
-
evidence changes its status, not the historical `🎫` reaction. For every
|
|
134
|
-
carrying any of this actor's marks, post or reuse a current status
|
|
135
|
-
unclaimed items and another actor's marks
|
|
160
|
+
evidence changes its status, not the historical `🎫` reaction. For every
|
|
161
|
+
unheld root carrying any of this actor's marks, post or reuse a current status
|
|
162
|
+
in that root's thread; unclaimed items, held roots, and another actor's marks
|
|
163
|
+
are not ours to reply to. Reconcile
|
|
136
164
|
each report as one unit: send or verify its current same-thread status reply,
|
|
137
165
|
then immediately update and read back each applicable root reaction before
|
|
138
166
|
advancing to the next report. Add `✅` as soon as any reported scope passes the
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|
package/dist/vite/client.js
CHANGED
|
@@ -2789,9 +2789,10 @@ function silenceConnectionResets() {
|
|
|
2789
2789
|
const message = String(e?.message ?? "");
|
|
2790
2790
|
return (code === "ECONNRESET" ||
|
|
2791
2791
|
code === "ECONNABORTED" ||
|
|
2792
|
+
code === "ECANCELED" ||
|
|
2792
2793
|
code === "EPIPE" ||
|
|
2793
2794
|
isClosedWebStreamController(err) ||
|
|
2794
|
-
/^(read ECONNRESET|write ECONNRESET|socket hang up|aborted|write EPIPE)$/i.test(message));
|
|
2795
|
+
/^(read ECONNRESET|write ECONNRESET|write ECANCELED|socket hang up|aborted|write EPIPE)$/i.test(message));
|
|
2795
2796
|
};
|
|
2796
2797
|
const isBenignErrorPayload = (payload) => {
|
|
2797
2798
|
const p = payload;
|
|
@@ -166,13 +166,13 @@ When `needsApproval` is omitted, behavior is byte-for-byte unchanged. There is n
|
|
|
166
166
|
|
|
167
167
|
This works the same way for legacy `parameters`-style actions and for schema-based actions. It also works the same way for the in-app agent, sub-agents, A2A, and MCP callers, because every agent surface routes through the same loop.
|
|
168
168
|
|
|
169
|
-
Calls
|
|
169
|
+
Calls over the [HTTP API](/docs/http-api) cannot give approval. When a call needs it, the route refuses with a `409` and `errorCode: "approval_required"`, whether the caller holds a bearer token, an API key, an MCP OAuth token, an org service token, or an A2A identity. A service identity can never approve. Only the app's own signed-in UI runs such a call directly, because the person using it is the one approving. A browser call proves it comes from that UI with a short-lived UI capability. A call that claims to be the app without one gets a `403` with `errorCode: "ui_capability_required"`, and the app's client mints the capability and retries once. To have an agent's calls approved, connect it over [MCP](/docs/external-agents) or have it ask in the app's agent chat.
|
|
170
170
|
|
|
171
171
|
### How the loop pauses {#loop}
|
|
172
172
|
|
|
173
173
|
When the agent calls a gated action and this specific call has **not** already been approved, the loop does **not** execute `run()`. Instead it:
|
|
174
174
|
|
|
175
|
-
1. Resolves the gate. For a predicate, the loop calls `needsApproval(
|
|
175
|
+
1. Resolves the gate. The loop first prepares the call: it validates the arguments and applies the schema's defaults and coercions. An invalid call stops here with its validation error, and nobody is asked to approve it. For a predicate, the loop then calls `needsApproval(args, ctx)` with the prepared arguments, the ones `run()` would receive. If that call throws an error, the loop treats it the same as a `true` result, so the action requires approval.
|
|
176
176
|
2. Emits a `tool_start` event (so the UI shows the call) followed immediately by an **`approval_required`** event, then stops the turn. The action's side effect never happens.
|
|
177
177
|
|
|
178
178
|
The `approval_required` event carries everything the client needs to render an affordance:
|
|
@@ -407,7 +407,7 @@ The four mechanisms are not interchangeable:
|
|
|
407
407
|
| --------------------------------- | ----------------------------------------------------- | -------------------------------- |
|
|
408
408
|
| `access` | Does the caller hold the declared role or membership? | All six dispatch sites |
|
|
409
409
|
| `authorize` | May this caller perform the operation at all? | All six dispatch sites |
|
|
410
|
-
| `needsApproval` | Should a human approve this specific permitted call? |
|
|
410
|
+
| `needsApproval` | Should a human approve this specific permitted call? | Every surface |
|
|
411
411
|
| `accessFilter()`/`assertAccess()` | Which rows may this permitted caller see or write? | Inside `run()`, per query or row |
|
|
412
412
|
|
|
413
413
|
A restricted write action typically needs both `authorize` and a row check. The first keeps non-coaches out of the operation. The second keeps a coach out of another org's plan. The row check is `assertAccess()`, or `access` when the row's id is an input field. `needsApproval` is a separate axis. It never restricts who may call, only whether the agent may proceed unattended.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.211.0-nightly-20261020183616",
|
|
4
4
|
"description": "The agentic application framework for building autonomous agents with intuitive UIs",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -711,8 +711,8 @@
|
|
|
711
711
|
"yaml": "^2.9.0",
|
|
712
712
|
"yjs": "^13.6.33",
|
|
713
713
|
"zod": "^4.3.6",
|
|
714
|
-
"@agent-native/agentkit": "^0.
|
|
715
|
-
"@agent-native/recap-cli": "^0.5.
|
|
714
|
+
"@agent-native/agentkit": "^0.211.0-nightly-20261020183616",
|
|
715
|
+
"@agent-native/recap-cli": "^0.5.77"
|
|
716
716
|
},
|
|
717
717
|
"devDependencies": {
|
|
718
718
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
@@ -782,7 +782,7 @@
|
|
|
782
782
|
"xlsx": "^0.18.5"
|
|
783
783
|
},
|
|
784
784
|
"peerDependencies": {
|
|
785
|
-
"@agent-native/core-corpus": "
|
|
785
|
+
"@agent-native/core-corpus": "0.211.0-nightly-20261020183616",
|
|
786
786
|
"@ai-sdk/anthropic": ">=3",
|
|
787
787
|
"@ai-sdk/cohere": ">=3",
|
|
788
788
|
"@ai-sdk/google": ">=3",
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|