@aitne-sh/aitne 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -147
- package/agent-assets/agent-profiles/conversational.md +23 -1
- package/agent-assets/agent-profiles/observer.md +15 -0
- package/agent-assets/agent-profiles/routine-fetch-window.md +128 -0
- package/agent-assets/agent-profiles/routine.md +16 -0
- package/agent-assets/agent-profiles/task.md +15 -0
- package/agent-assets/docs/concepts/auth-health.md +25 -9
- package/agent-assets/docs/concepts/backends-and-tiers.md +40 -4
- package/agent-assets/docs/concepts/costs-and-quotas.md +87 -25
- package/agent-assets/docs/concepts/delegated-mode.md +7 -13
- package/agent-assets/docs/concepts/memory-model.md +14 -1
- package/agent-assets/docs/concepts/observations.md +19 -1
- package/agent-assets/docs/concepts/process-keys.md +5 -0
- package/agent-assets/docs/concepts/routines.md +22 -10
- package/agent-assets/docs/concepts/safety-model.md +3 -8
- package/agent-assets/docs/concepts/skills.md +36 -1
- package/agent-assets/docs/features/integrations/calendar.md +74 -3
- package/agent-assets/docs/features/integrations/git.md +4 -4
- package/agent-assets/docs/features/integrations/github.md +75 -107
- package/agent-assets/docs/features/lifestyle/git.md +169 -22
- package/agent-assets/docs/features/messaging/overview.md +10 -1
- package/agent-assets/docs/features/routines/morning-routine.md +1 -1
- package/agent-assets/docs/getting-started/01-what-is-this.md +30 -12
- package/agent-assets/docs/getting-started/02-first-steps.md +15 -4
- package/agent-assets/docs/getting-started/03-what-can-this-do.md +17 -2
- package/agent-assets/docs/guides/install-and-run.md +10 -1
- package/agent-assets/docs/guides/setup-wizard.md +43 -6
- package/agent-assets/docs/guides/switch-default-backend.md +7 -3
- package/agent-assets/docs/reference/skills.md +10 -1
- package/agent-assets/docs/troubleshooting/auth-failed.md +27 -8
- package/agent-assets/docs/troubleshooting/quota-exhausted.md +35 -12
- package/agent-assets/skills/context/SKILL.md +6 -0
- package/agent-assets/skills/external-services/SKILL.md +4 -0
- package/agent-assets/skills/external-services/SKILL.native.claude.md +320 -0
- package/agent-assets/skills/external-services/SKILL.native.codex.md +243 -0
- package/agent-assets/skills/external-services/SKILL.native.gemini.md +237 -0
- package/agent-assets/skills/mail/SKILL.md +42 -14
- package/agent-assets/skills/mail/SKILL.native.claude.md +175 -0
- package/agent-assets/skills/mail/SKILL.native.codex.md +165 -0
- package/agent-assets/skills/mail/SKILL.native.gemini.md +169 -0
- package/agent-assets/skills/management-task-modify/SKILL.md +2 -1
- package/agent-assets/skills/management-task-stop/SKILL.md +2 -2
- package/agent-assets/skills/notify/SKILL.md +4 -4
- package/agent-assets/skills/notion/SKILL.md +6 -0
- package/agent-assets/skills/notion/SKILL.native.claude.md +202 -0
- package/agent-assets/skills/notion/SKILL.native.codex.md +166 -0
- package/agent-assets/skills/notion/SKILL.native.gemini.md +167 -0
- package/agent-assets/skills/observations/SKILL.md +7 -0
- package/agent-assets/skills/project-doc/SKILL.md +6 -0
- package/agent-assets/skills/reading/SKILL.md +2 -0
- package/agent-assets/skills/roadmap/SKILL.md +7 -0
- package/agent-assets/skills/today/SKILL.md +7 -0
- package/agent-assets/skills/user-interview/SKILL.md +1 -1
- package/agent-assets/skills/user-profile/SKILL.md +7 -0
- package/agent-assets/task-flows/_partials/calendar-acquire.google_calendar.md +119 -0
- package/agent-assets/task-flows/_partials/calendar-acquire.outlook_calendar.md +101 -0
- package/agent-assets/task-flows/_partials/mail-acquire.gmail.md +113 -0
- package/agent-assets/task-flows/_partials/mail-acquire.outlook_mail.md +97 -0
- package/agent-assets/task-flows/_partials/notion-acquire.notion.md +104 -0
- package/agent-assets/task-flows/git.project.refresh_architecture.md +24 -1
- package/agent-assets/task-flows/message.received.dm.md +3 -0
- package/agent-assets/task-flows/message.received.dm.native.claude.md +76 -0
- package/agent-assets/task-flows/message.received.dm.native.codex.md +57 -0
- package/agent-assets/task-flows/message.received.dm.native.gemini.md +70 -0
- package/agent-assets/task-flows/message.received.dm_first.md +3 -0
- package/agent-assets/task-flows/message.received.dm_first.native.claude.md +56 -0
- package/agent-assets/task-flows/message.received.dm_first.native.codex.md +48 -0
- package/agent-assets/task-flows/message.received.dm_first.native.gemini.md +54 -0
- package/agent-assets/task-flows/routine.evening_review.md +28 -1
- package/agent-assets/task-flows/routine.fetch_window.md +93 -0
- package/agent-assets/task-flows/routine.hourly_check.md +44 -5
- package/agent-assets/task-flows/routine.monthly_review.md +13 -2
- package/agent-assets/task-flows/routine.morning_routine.md +55 -42
- package/agent-assets/task-flows/routine.morning_routine_initial.md +37 -38
- package/agent-assets/task-flows/routine.roadmap_refresh.md +38 -46
- package/agent-assets/task-flows/routine.today_refresh.md +53 -96
- package/agent-assets/task-flows/routine.weekly_review.md +40 -17
- package/agent-assets/task-flows/scheduled.dm.md +13 -11
- package/agent-assets/task-flows/scheduled.task.md +2 -2
- package/agent-assets/task-flows/setup.initial.md +5 -4
- package/agent-assets/task-flows/setup.update.md +1 -1
- package/agent-assets/templates/README.md +13 -6
- package/bin/aitne.mjs +1 -1
- package/package.json +22 -39
- package/scripts/check-redaction-coverage.mjs +0 -0
- package/scripts/message-discipline-digest.mjs +0 -0
- package/scripts/regen-skill-fixtures.mjs +39 -0
- package/scripts/remint-roadmap-ids.mjs +0 -0
- package/agent-assets/task-flows/routine.hourly_check.delegated.claude.md +0 -405
- package/agent-assets/task-flows/routine.hourly_check.delegated.codex.md +0 -400
- package/agent-assets/task-flows/routine.hourly_check.delegated.gemini.md +0 -404
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: calendar-acquire.google_calendar
|
|
3
|
+
description: Acquire a Google Calendar event window per <acquisition-plan> row.
|
|
4
|
+
spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Google Calendar acquisition
|
|
8
|
+
|
|
9
|
+
For every `<fetch integration="google_calendar" ...>` row in
|
|
10
|
+
`<acquisition-plan>`, take the branch below that matches the row's `mode`
|
|
11
|
+
attribute. Calendar rows do not fan out per account today — the dispatcher
|
|
12
|
+
emits one row per active provider, scoped to the bound primary calendar.
|
|
13
|
+
|
|
14
|
+
Note on coverage: routines whose calendar window is already covered by
|
|
15
|
+
`ContextBuilder.buildCalendarBlock` (the `<calendar_events_*>` blocks) do
|
|
16
|
+
**not** appear in the pre-pass plan for the same window — that would
|
|
17
|
+
double-fetch. The catalog only emits drift / retrospective / imminent
|
|
18
|
+
windows for the pre-pass.
|
|
19
|
+
|
|
20
|
+
POST every returned event to `http://localhost:8321/api/observations`:
|
|
21
|
+
|
|
22
|
+
- `source` = `"google_calendar:<calendarId>"` (use `"primary"` when the
|
|
23
|
+
provider returns no explicit id)
|
|
24
|
+
- `ref` = provider-side stable event id
|
|
25
|
+
- `changeType` = `"created"` for fresh events; `"modified"` when the
|
|
26
|
+
payload updates an existing `(source, ref)`; `"deleted"` for cancelled
|
|
27
|
+
events (payload = `{ "kind": "calendar", "providerId": "<calendarId>",
|
|
28
|
+
"raw": { "deletedAt": "<iso>" } }`)
|
|
29
|
+
- `actor` = `"agent"`
|
|
30
|
+
- `payload` = `{ "kind": "calendar", "providerId": "<calendarId>",
|
|
31
|
+
"raw": { "title": ..., "start": ..., "end": ...,
|
|
32
|
+
"attendees": [...], "status": ... } }`
|
|
33
|
+
|
|
34
|
+
Server computes the dedup hash from `(source, payload)`. Response shape:
|
|
35
|
+
|
|
36
|
+
- `200 {action: "created"|"modified"}` — fresh / updated row; count in `posted`.
|
|
37
|
+
- `409 {error: "duplicate"}` — identical payload already pending; count in `duplicates`.
|
|
38
|
+
- `409 {error: "integration_flip_in_progress"}` — append
|
|
39
|
+
`{type:"flip-locked","integration":"google_calendar"}` to `errors`
|
|
40
|
+
and move on.
|
|
41
|
+
|
|
42
|
+
<!-- mode:direct:google_calendar -->
|
|
43
|
+
GET `http://localhost:8321/api/calendar/events<query>` where `<query>` is
|
|
44
|
+
the literal `query` attribute of the `<fetch>` row (e.g.
|
|
45
|
+
`?date=2026-05-11&days=1` or `?date=2026-05-04&days=7`). The route
|
|
46
|
+
accepts `date=YYYY-MM-DD` (or `today`) plus `days=N` (≤90); `timeMin`
|
|
47
|
+
/ `timeMax` are NOT recognised. The daemon returns `{ "events": [...] }`;
|
|
48
|
+
map each event into one observation POST as specified above.
|
|
49
|
+
<!-- /mode:direct:google_calendar -->
|
|
50
|
+
|
|
51
|
+
<!-- mode:delegated-same:google_calendar -->
|
|
52
|
+
The connector is bound to your own session backend. Use the in-session
|
|
53
|
+
connector surface your skills document; the `<fetch>` row's `query`
|
|
54
|
+
attribute carries the catalog's `delegated` form (e.g.
|
|
55
|
+
`timeMin="..." timeMax="..." maxResults=50`). Translate it into the args
|
|
56
|
+
your bound surface accepts. POST every returned event as specified above.
|
|
57
|
+
<!-- /mode:delegated-same:google_calendar -->
|
|
58
|
+
|
|
59
|
+
<!-- mode:delegated-cross:google_calendar -->
|
|
60
|
+
The connector is bound to a different backend than this session — reach
|
|
61
|
+
it through the daemon's delegation proxy. POST to
|
|
62
|
+
`http://localhost:8321/api/integrations/google_calendar/exec` with the
|
|
63
|
+
following body (substitute the row's `query` into `task`):
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"task": "List Google Calendar events for the window <query>. Return id, title, start, end, attendees (email + responseStatus), and status. Up to 100 events.",
|
|
68
|
+
"outputSchema": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": ["events"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"events": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["id"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"id": { "type": "string" },
|
|
79
|
+
"title": { "type": "string" },
|
|
80
|
+
"start": { "type": "string" },
|
|
81
|
+
"end": { "type": "string" },
|
|
82
|
+
"status": { "type": "string" },
|
|
83
|
+
"attendees": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"properties": {
|
|
88
|
+
"email": { "type": "string" },
|
|
89
|
+
"responseStatus": { "type": "string" }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"maxToolCalls": 3,
|
|
99
|
+
"cacheable": true
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Map each item in `result.events[]` to one observation POST.
|
|
104
|
+
<!-- /mode:delegated-cross:google_calendar -->
|
|
105
|
+
|
|
106
|
+
<!-- mode:native:google_calendar -->
|
|
107
|
+
The connector is bound natively to your own session backend. Use the
|
|
108
|
+
in-session connector surface your skills document — same call shape as
|
|
109
|
+
`delegated-same`. The daemon does not proxy. POST every returned event as
|
|
110
|
+
specified above.
|
|
111
|
+
<!-- /mode:native:google_calendar -->
|
|
112
|
+
|
|
113
|
+
<!-- mode:disabled:google_calendar -->
|
|
114
|
+
Defensive no-op. The dispatcher filters disabled integrations out of
|
|
115
|
+
`<acquisition-plan>`. If a `<fetch integration="google_calendar">` row
|
|
116
|
+
still reaches this branch, skip it and append
|
|
117
|
+
`{"type":"unexpected-row","integration":"google_calendar","reason":"disabled-row-emitted"}`
|
|
118
|
+
to your `errors` array.
|
|
119
|
+
<!-- /mode:disabled:google_calendar -->
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: calendar-acquire.outlook_calendar
|
|
3
|
+
description: Acquire an Outlook Calendar event window per <acquisition-plan> row.
|
|
4
|
+
spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.4
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Outlook Calendar acquisition
|
|
8
|
+
|
|
9
|
+
For every `<fetch integration="outlook_calendar" ...>` row in
|
|
10
|
+
`<acquisition-plan>`, take the branch below that matches the row's `mode`
|
|
11
|
+
attribute. Calendar rows do not fan out per account today — the dispatcher
|
|
12
|
+
emits one row per active provider, scoped to the bound primary calendar.
|
|
13
|
+
|
|
14
|
+
Outlook Calendar is a **user-managed** integration: the daemon has no
|
|
15
|
+
delegation proxy (no `/api/integrations/outlook_calendar/exec` exists).
|
|
16
|
+
The four non-disabled branches therefore split into two real flows:
|
|
17
|
+
|
|
18
|
+
- `direct` → the daemon's Outlook calendar route
|
|
19
|
+
(`/api/calendar/outlook/events`).
|
|
20
|
+
- `delegated-same`, `delegated-cross`, `native` → use the in-session
|
|
21
|
+
connector surface your skills document. The user picks the binding (an
|
|
22
|
+
in-session connector, a local CLI invoked via a skill, a custom
|
|
23
|
+
script); the partial states the intent, not specific tool names. If no
|
|
24
|
+
surface is bound, record an error and continue.
|
|
25
|
+
|
|
26
|
+
Note on coverage: when `ContextBuilder.buildCalendarBlock` already covers
|
|
27
|
+
the window via `<calendar_events_*>` (multi-provider after §6.6), the
|
|
28
|
+
catalog skips the pre-pass row to avoid double-fetching. The pre-pass
|
|
29
|
+
only ships drift / retrospective / imminent windows.
|
|
30
|
+
|
|
31
|
+
POST every returned event to `http://localhost:8321/api/observations`:
|
|
32
|
+
|
|
33
|
+
- `source` = `"outlook_calendar:<calendarId>"` (use `"primary"` when
|
|
34
|
+
the provider returns no explicit id)
|
|
35
|
+
- `ref` = provider-side stable event id
|
|
36
|
+
- `changeType` = `"created"` for fresh events; `"modified"` when the
|
|
37
|
+
payload updates an existing `(source, ref)`; `"deleted"` for cancelled
|
|
38
|
+
events
|
|
39
|
+
- `actor` = `"agent"`
|
|
40
|
+
- `payload` = `{ "kind": "calendar", "providerId": "<calendarId>",
|
|
41
|
+
"raw": { "title": ..., "start": ..., "end": ...,
|
|
42
|
+
"attendees": [...], "status": ... } }`
|
|
43
|
+
|
|
44
|
+
Server computes the dedup hash from `(source, payload)`. Response shape:
|
|
45
|
+
|
|
46
|
+
- `200 {action: "created"|"modified"}` — fresh / updated row; count in `posted`.
|
|
47
|
+
- `409 {error: "duplicate"}` — identical payload already pending; count in `duplicates`.
|
|
48
|
+
- `409 {error: "integration_flip_in_progress"}` — append
|
|
49
|
+
`{type:"flip-locked","integration":"outlook_calendar"}` to `errors`
|
|
50
|
+
and move on.
|
|
51
|
+
|
|
52
|
+
<!-- mode:direct:outlook_calendar -->
|
|
53
|
+
GET `http://localhost:8321/api/calendar/outlook/events<query>` where
|
|
54
|
+
`<query>` is the literal `query` attribute of the `<fetch>` row (e.g.
|
|
55
|
+
`?date=2026-05-11&days=1` or `?date=2026-05-04&days=7`). The route
|
|
56
|
+
accepts `date=YYYY-MM-DD` (or `today`) plus `days=N` (≤90); `timeMin`
|
|
57
|
+
/ `timeMax` are NOT recognised. The daemon returns
|
|
58
|
+
`{ "events": [...] }`; map each event into one observation POST as
|
|
59
|
+
specified above.
|
|
60
|
+
<!-- /mode:direct:outlook_calendar -->
|
|
61
|
+
|
|
62
|
+
<!-- mode:delegated-same:outlook_calendar -->
|
|
63
|
+
The integration is bound to your own session backend. Use the in-session
|
|
64
|
+
connector surface your skills document for Outlook Calendar; the
|
|
65
|
+
`<fetch>` row's `query` attribute carries the catalog's `delegated` form
|
|
66
|
+
(e.g. `startDateTime=... endDateTime=...`). Translate it into the args
|
|
67
|
+
your bound surface accepts. POST every returned event as specified above.
|
|
68
|
+
|
|
69
|
+
If no Outlook Calendar surface is bound, append
|
|
70
|
+
`{"type":"no-surface","integration":"outlook_calendar"}` to your `errors`
|
|
71
|
+
array and continue with the next row. Do NOT halt the pre-pass.
|
|
72
|
+
<!-- /mode:delegated-same:outlook_calendar -->
|
|
73
|
+
|
|
74
|
+
<!-- mode:delegated-cross:outlook_calendar -->
|
|
75
|
+
Outlook Calendar is user-managed, so the daemon does not host a
|
|
76
|
+
delegation proxy. The dispatcher should not have emitted a
|
|
77
|
+
`delegated-cross` row for this integration — if you see one, treat it
|
|
78
|
+
exactly like `delegated-same`: use whichever in-session connector
|
|
79
|
+
surface your skills document for Outlook Calendar. If nothing is bound,
|
|
80
|
+
append `{"type":"no-surface","integration":"outlook_calendar"}` to
|
|
81
|
+
`errors` and continue.
|
|
82
|
+
<!-- /mode:delegated-cross:outlook_calendar -->
|
|
83
|
+
|
|
84
|
+
<!-- mode:native:outlook_calendar -->
|
|
85
|
+
The integration is bound natively to your own session backend. Use the
|
|
86
|
+
in-session connector surface your skills document — same call shape as
|
|
87
|
+
`delegated-same`. The daemon does not proxy. POST every returned event as
|
|
88
|
+
specified above.
|
|
89
|
+
|
|
90
|
+
If no Outlook Calendar surface is bound, append
|
|
91
|
+
`{"type":"no-surface","integration":"outlook_calendar"}` to `errors` and
|
|
92
|
+
continue.
|
|
93
|
+
<!-- /mode:native:outlook_calendar -->
|
|
94
|
+
|
|
95
|
+
<!-- mode:disabled:outlook_calendar -->
|
|
96
|
+
Defensive no-op. The dispatcher filters disabled integrations out of
|
|
97
|
+
`<acquisition-plan>`. If a `<fetch integration="outlook_calendar">` row
|
|
98
|
+
still reaches this branch, skip it and append
|
|
99
|
+
`{"type":"unexpected-row","integration":"outlook_calendar","reason":"disabled-row-emitted"}`
|
|
100
|
+
to your `errors` array.
|
|
101
|
+
<!-- /mode:disabled:outlook_calendar -->
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mail-acquire.gmail
|
|
3
|
+
description: Acquire a Gmail message window per <acquisition-plan> row.
|
|
4
|
+
spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.1
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Gmail acquisition
|
|
8
|
+
|
|
9
|
+
For every `<fetch integration="gmail" ...>` row in `<acquisition-plan>`, take
|
|
10
|
+
the branch below that matches the row's `mode` attribute and acquire the
|
|
11
|
+
window described by `query`. Each row carries `account="<accountId>"` — apply
|
|
12
|
+
the query against that account only, do not pool across accounts.
|
|
13
|
+
|
|
14
|
+
POST every returned message to `http://localhost:8321/api/observations` per
|
|
15
|
+
the contract in your agent profile (the response from the upstream call IS
|
|
16
|
+
the payload; do not summarise or rank). Use:
|
|
17
|
+
|
|
18
|
+
- `source` = `"gmail:<accountId>"`
|
|
19
|
+
- `ref` = provider-side stable message id
|
|
20
|
+
- `changeType` = `"created"` for fresh items; `"modified"` when the row updates
|
|
21
|
+
a payload the server already has under the same `(source, ref)`
|
|
22
|
+
- `actor` = `"agent"`
|
|
23
|
+
- `payload` = `{ "kind": "mail", "providerId": "<accountId>", "raw": {
|
|
24
|
+
"subject": ..., "from": ..., "snippet": ...,
|
|
25
|
+
"date": ... } }`
|
|
26
|
+
|
|
27
|
+
Do NOT compute the dedup hash — the server derives it from `(source, payload)`.
|
|
28
|
+
The response body shape distinguishes three outcomes:
|
|
29
|
+
|
|
30
|
+
- `200 {action: "created"}` — fresh row inserted; count it in `posted`.
|
|
31
|
+
- `200 {action: "modified"}` — same `(source, ref)` pending row existed
|
|
32
|
+
with a different payload; the row was updated and re-summarized.
|
|
33
|
+
Count it in `posted`.
|
|
34
|
+
- `409 {error: "duplicate"}` — same `(source, ref)` pending row already
|
|
35
|
+
stores an identical payload. Nothing was written; count it in
|
|
36
|
+
`duplicates` and move on.
|
|
37
|
+
- `409 {error: "integration_flip_in_progress"}` — a mode flip is
|
|
38
|
+
draining for this integration. Record
|
|
39
|
+
`{type:"flip-locked","integration":"gmail","account":"<accountId>"}`
|
|
40
|
+
in `errors` and continue with the next row (the parent routine will
|
|
41
|
+
retry on the next tick).
|
|
42
|
+
|
|
43
|
+
<!-- mode:direct:gmail -->
|
|
44
|
+
GET `http://localhost:8321/api/mail/<accountId>/messages<query>` where
|
|
45
|
+
`<query>` is the literal `query` attribute of the `<fetch>` row (e.g.
|
|
46
|
+
`?since=2026-05-11T00:00:00.000Z&limit=20` or
|
|
47
|
+
`?since=2026-05-11T10:00:00.000Z&unreadOnly=true&limit=10` or
|
|
48
|
+
`?folder=sent&since=2026-05-11T00:00:00.000Z&limit=30`). The route
|
|
49
|
+
accepts `since` (ISO 8601 datetime), `limit`, `folder`, `q`,
|
|
50
|
+
`unreadOnly` — `days=…` is NOT recognised. The daemon returns
|
|
51
|
+
`{ "messages": [...] }`; map each item to one POST as specified above.
|
|
52
|
+
<!-- /mode:direct:gmail -->
|
|
53
|
+
|
|
54
|
+
<!-- mode:delegated-same:gmail -->
|
|
55
|
+
The Gmail connector is bound to your own session backend. Use the in-session
|
|
56
|
+
connector surface your skills document for this integration; the `<fetch>`
|
|
57
|
+
row's `query` attribute carries the catalog's `delegated` form (e.g.
|
|
58
|
+
`q="newer_than:1d" maxResults=20`) — translate it into the args your bound
|
|
59
|
+
surface accepts. POST every returned message as specified above. The daemon
|
|
60
|
+
does not proxy in this branch.
|
|
61
|
+
<!-- /mode:delegated-same:gmail -->
|
|
62
|
+
|
|
63
|
+
<!-- mode:delegated-cross:gmail -->
|
|
64
|
+
The connector is bound to a different backend than this session, so reach it
|
|
65
|
+
through the daemon's delegation proxy. POST to
|
|
66
|
+
`http://localhost:8321/api/integrations/gmail/exec` with the following body
|
|
67
|
+
(substitute the row's `query` and `accountId` into `task`):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"task": "On account <accountId>, search Gmail with the query expression <query> and return id, subject, from, snippet, date for each message. Up to 30 messages.",
|
|
72
|
+
"outputSchema": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"required": ["messages"],
|
|
75
|
+
"properties": {
|
|
76
|
+
"messages": {
|
|
77
|
+
"type": "array",
|
|
78
|
+
"items": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"required": ["id"],
|
|
81
|
+
"properties": {
|
|
82
|
+
"id": { "type": "string" },
|
|
83
|
+
"subject": { "type": "string" },
|
|
84
|
+
"from": { "type": "string" },
|
|
85
|
+
"snippet": { "type": "string" },
|
|
86
|
+
"date": { "type": "string" }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"maxToolCalls": 3,
|
|
93
|
+
"cacheable": true
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Map each item in `result.messages[]` to one observation POST.
|
|
98
|
+
<!-- /mode:delegated-cross:gmail -->
|
|
99
|
+
|
|
100
|
+
<!-- mode:native:gmail -->
|
|
101
|
+
The connector is bound natively to your own session backend. Use the
|
|
102
|
+
in-session connector surface your skills document — same call shape as
|
|
103
|
+
`delegated-same`. The daemon does not proxy. POST every returned message as
|
|
104
|
+
specified above.
|
|
105
|
+
<!-- /mode:native:gmail -->
|
|
106
|
+
|
|
107
|
+
<!-- mode:disabled:gmail -->
|
|
108
|
+
Defensive no-op. The dispatcher filters disabled integrations out of
|
|
109
|
+
`<acquisition-plan>`, so no `<fetch integration="gmail">` row should ever
|
|
110
|
+
land in this branch. If one does, skip it and append
|
|
111
|
+
`{"type":"unexpected-row","integration":"gmail","reason":"disabled-row-emitted"}`
|
|
112
|
+
to your `errors` array.
|
|
113
|
+
<!-- /mode:disabled:gmail -->
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mail-acquire.outlook_mail
|
|
3
|
+
description: Acquire an Outlook Mail message window per <acquisition-plan> row.
|
|
4
|
+
spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Outlook Mail acquisition
|
|
8
|
+
|
|
9
|
+
For every `<fetch integration="outlook_mail" ...>` row in `<acquisition-plan>`,
|
|
10
|
+
take the branch below that matches the row's `mode` attribute. Each row
|
|
11
|
+
carries `account="<accountId>"` — apply the query to that account only.
|
|
12
|
+
|
|
13
|
+
Outlook Mail is a **user-managed** integration: the daemon has no
|
|
14
|
+
delegation proxy for it (no `/api/integrations/outlook_mail/exec` exists).
|
|
15
|
+
The four non-disabled branches therefore split into two real flows:
|
|
16
|
+
|
|
17
|
+
- `direct` → the unified daemon mail route (transparently handles Outlook
|
|
18
|
+
accounts).
|
|
19
|
+
- `delegated-same`, `delegated-cross`, `native` → use the in-session
|
|
20
|
+
connector surface your skills document. The user picks the binding (an
|
|
21
|
+
in-session connector, a local CLI invoked via a skill, a custom script);
|
|
22
|
+
this partial states the intent, not specific tool names. If no surface
|
|
23
|
+
is bound, record an error and continue.
|
|
24
|
+
|
|
25
|
+
POST every returned message to `http://localhost:8321/api/observations`:
|
|
26
|
+
|
|
27
|
+
- `source` = `"outlook_mail:<accountId>"`
|
|
28
|
+
- `ref` = provider-side stable message id
|
|
29
|
+
- `changeType` = `"created"` for fresh items; `"modified"` when the row
|
|
30
|
+
updates a payload already known under `(source, ref)`
|
|
31
|
+
- `actor` = `"agent"`
|
|
32
|
+
- `payload` = `{ "kind": "mail", "providerId": "<accountId>", "raw": {
|
|
33
|
+
"subject": ..., "from": ..., "snippet": ...,
|
|
34
|
+
"date": ... } }`
|
|
35
|
+
|
|
36
|
+
The server computes the dedup hash from `(source, payload)`. Response
|
|
37
|
+
shape:
|
|
38
|
+
|
|
39
|
+
- `200 {action: "created"|"modified"}` — fresh / updated row; count in `posted`.
|
|
40
|
+
- `409 {error: "duplicate"}` — identical payload already pending; count in `duplicates`.
|
|
41
|
+
- `409 {error: "integration_flip_in_progress"}` — append
|
|
42
|
+
`{type:"flip-locked","integration":"outlook_mail","account":"<accountId>"}`
|
|
43
|
+
to `errors` and move on; do not retry inline.
|
|
44
|
+
|
|
45
|
+
<!-- mode:direct:outlook_mail -->
|
|
46
|
+
GET `http://localhost:8321/api/mail/<accountId>/messages<query>` where
|
|
47
|
+
`<query>` is the literal `query` attribute of the `<fetch>` row (e.g.
|
|
48
|
+
`?since=2026-05-11T00:00:00.000Z&limit=20` or
|
|
49
|
+
`?folder=sent&since=2026-05-11T00:00:00.000Z&limit=30`). The route
|
|
50
|
+
accepts `since` (ISO 8601), `limit`, `folder`, `q`, `unreadOnly` — it
|
|
51
|
+
does NOT accept `days=…`. The daemon returns `{ "messages": [...] }`
|
|
52
|
+
regardless of the underlying provider; map each item into one
|
|
53
|
+
observation POST as specified above.
|
|
54
|
+
<!-- /mode:direct:outlook_mail -->
|
|
55
|
+
|
|
56
|
+
<!-- mode:delegated-same:outlook_mail -->
|
|
57
|
+
The integration is bound to your own session backend. Use the in-session
|
|
58
|
+
connector surface your skills document for Outlook Mail. The `<fetch>`
|
|
59
|
+
row's `query` attribute carries the catalog's `delegated` form (e.g.
|
|
60
|
+
`filter=receivedDateTime ge 2026-05-11T00:00:00Z`) — translate it into the
|
|
61
|
+
args your bound surface accepts. POST every returned message as specified
|
|
62
|
+
above.
|
|
63
|
+
|
|
64
|
+
If no Outlook Mail surface is bound on this backend, append
|
|
65
|
+
`{"type":"no-surface","integration":"outlook_mail","account":"<accountId>"}`
|
|
66
|
+
to your `errors` array and continue with the next row. Do NOT halt the
|
|
67
|
+
pre-pass; the parent routine continues with whatever observations the rest
|
|
68
|
+
of the plan produced.
|
|
69
|
+
<!-- /mode:delegated-same:outlook_mail -->
|
|
70
|
+
|
|
71
|
+
<!-- mode:delegated-cross:outlook_mail -->
|
|
72
|
+
Outlook Mail is user-managed, so the daemon does not host a delegation
|
|
73
|
+
proxy. The dispatcher should not have emitted a `delegated-cross` row for
|
|
74
|
+
this integration — if you see one, treat it exactly like
|
|
75
|
+
`delegated-same`: use whichever in-session surface your skills document
|
|
76
|
+
for Outlook Mail. If nothing is bound, append
|
|
77
|
+
`{"type":"no-surface","integration":"outlook_mail","account":"<accountId>"}`
|
|
78
|
+
to `errors` and continue.
|
|
79
|
+
<!-- /mode:delegated-cross:outlook_mail -->
|
|
80
|
+
|
|
81
|
+
<!-- mode:native:outlook_mail -->
|
|
82
|
+
The integration is bound natively to your own session backend. Use the
|
|
83
|
+
in-session connector surface your skills document for Outlook Mail —
|
|
84
|
+
same call shape as `delegated-same`. The daemon does not proxy.
|
|
85
|
+
|
|
86
|
+
If no Outlook Mail surface is bound, append
|
|
87
|
+
`{"type":"no-surface","integration":"outlook_mail","account":"<accountId>"}`
|
|
88
|
+
to `errors` and continue.
|
|
89
|
+
<!-- /mode:native:outlook_mail -->
|
|
90
|
+
|
|
91
|
+
<!-- mode:disabled:outlook_mail -->
|
|
92
|
+
Defensive no-op. The dispatcher filters disabled integrations out of
|
|
93
|
+
`<acquisition-plan>`. If a `<fetch integration="outlook_mail">` row still
|
|
94
|
+
reaches this branch, skip it and append
|
|
95
|
+
`{"type":"unexpected-row","integration":"outlook_mail","reason":"disabled-row-emitted"}`
|
|
96
|
+
to your `errors` array.
|
|
97
|
+
<!-- /mode:disabled:outlook_mail -->
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notion-acquire.notion
|
|
3
|
+
description: Acquire recently-updated Notion pages per <acquisition-plan> row.
|
|
4
|
+
spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Notion acquisition
|
|
8
|
+
|
|
9
|
+
For every `<fetch integration="notion" ...>` row in `<acquisition-plan>`,
|
|
10
|
+
take the branch below that matches the row's `mode` attribute. Notion rows
|
|
11
|
+
do not fan out per account — the dispatcher emits one row per workspace.
|
|
12
|
+
|
|
13
|
+
POST every returned page to `http://localhost:8321/api/observations`:
|
|
14
|
+
|
|
15
|
+
- `source` = `"notion:<workspaceId>"` (use `"default"` when the daemon
|
|
16
|
+
reports no explicit workspace id)
|
|
17
|
+
- `ref` = Notion page id (stable UUID)
|
|
18
|
+
- `changeType` = `"created"` for fresh pages; `"modified"` when the
|
|
19
|
+
payload updates an existing `(source, ref)`
|
|
20
|
+
- `actor` = `"agent"`
|
|
21
|
+
- `payload` = `{ "kind": "notion", "providerId": "<workspaceId>",
|
|
22
|
+
"raw": { "title": ..., "last_edited": ...,
|
|
23
|
+
"parent": ..., "url": ... } }`
|
|
24
|
+
|
|
25
|
+
The server computes the dedup hash from `(source, payload)`. Response shape:
|
|
26
|
+
|
|
27
|
+
- `200 {action: "created"|"modified"}` — fresh / updated row; count in `posted`.
|
|
28
|
+
- `409 {error: "duplicate"}` — identical payload already pending; count in `duplicates`.
|
|
29
|
+
- `409 {error: "integration_flip_in_progress"}` — append
|
|
30
|
+
`{type:"flip-locked","integration":"notion"}` to `errors` and move on.
|
|
31
|
+
|
|
32
|
+
<!-- mode:direct:notion -->
|
|
33
|
+
GET `http://localhost:8321/api/notion/search<query>` where `<query>` is
|
|
34
|
+
the literal `query` attribute of the `<fetch>` row (e.g.
|
|
35
|
+
`?page_size=50&sort=descending` or `?page_size=20&sort=descending`).
|
|
36
|
+
The route accepts `q`, `type`, `sort` (`ascending` / `descending`),
|
|
37
|
+
`page_size` (≤100), `start_cursor` — it has NO time filter, so do the
|
|
38
|
+
window cutoff client-side. The daemon returns `{ "results": [...] }`
|
|
39
|
+
sorted by `last_edited_time` descending; filter to entries whose
|
|
40
|
+
`last_edited_time` is at or after the window the `<fetch>` row's
|
|
41
|
+
window symbol implies (`updated_24h` → today's agent-day start,
|
|
42
|
+
`updated_1h` → the current hour boundary), then map each surviving
|
|
43
|
+
page into one observation POST as specified above.
|
|
44
|
+
<!-- /mode:direct:notion -->
|
|
45
|
+
|
|
46
|
+
<!-- mode:delegated-same:notion -->
|
|
47
|
+
The connector is bound to your own session backend. Use the in-session
|
|
48
|
+
connector surface your skills document for Notion; the `<fetch>` row's
|
|
49
|
+
`query` attribute carries the catalog's `delegated` form
|
|
50
|
+
(e.g. `last_edited_time>=<iso>`). Translate it into the args your bound
|
|
51
|
+
surface accepts. POST every returned page as specified above.
|
|
52
|
+
<!-- /mode:delegated-same:notion -->
|
|
53
|
+
|
|
54
|
+
<!-- mode:delegated-cross:notion -->
|
|
55
|
+
The connector is bound to a different backend than this session — reach
|
|
56
|
+
it through the daemon's delegation proxy. POST to
|
|
57
|
+
`http://localhost:8321/api/integrations/notion/exec` with the following
|
|
58
|
+
body (substitute the row's `query` into `task`):
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"task": "Search Notion for pages with last_edited_time matching <query>. Return id, title, last_edited, parent, url for each page. Up to 50 pages.",
|
|
63
|
+
"outputSchema": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"required": ["pages"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"pages": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"items": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"required": ["id"],
|
|
72
|
+
"properties": {
|
|
73
|
+
"id": { "type": "string" },
|
|
74
|
+
"title": { "type": "string" },
|
|
75
|
+
"last_edited": { "type": "string" },
|
|
76
|
+
"parent": { "type": "string" },
|
|
77
|
+
"url": { "type": "string" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"maxToolCalls": 3,
|
|
84
|
+
"cacheable": true
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Map each item in `result.pages[]` to one observation POST.
|
|
89
|
+
<!-- /mode:delegated-cross:notion -->
|
|
90
|
+
|
|
91
|
+
<!-- mode:native:notion -->
|
|
92
|
+
The connector is bound natively to your own session backend. Use the
|
|
93
|
+
in-session connector surface your skills document — same call shape as
|
|
94
|
+
`delegated-same`. The daemon does not proxy. POST every returned page as
|
|
95
|
+
specified above.
|
|
96
|
+
<!-- /mode:native:notion -->
|
|
97
|
+
|
|
98
|
+
<!-- mode:disabled:notion -->
|
|
99
|
+
Defensive no-op. The dispatcher filters disabled integrations out of
|
|
100
|
+
`<acquisition-plan>`. If a `<fetch integration="notion">` row still
|
|
101
|
+
reaches this branch, skip it and append
|
|
102
|
+
`{"type":"unexpected-row","integration":"notion","reason":"disabled-row-emitted"}`
|
|
103
|
+
to your `errors` array.
|
|
104
|
+
<!-- /mode:disabled:notion -->
|
|
@@ -10,6 +10,28 @@ surgical merge — you submit only the new section body and it replaces the
|
|
|
10
10
|
marker-bracketed Architecture block in place. Other sections (Summary,
|
|
11
11
|
Notable Changes, Daily Activity Log) are preserved automatically.
|
|
12
12
|
|
|
13
|
+
## Tools available to you
|
|
14
|
+
|
|
15
|
+
This session runs under a **read-only clamp**. Your only write surface is the
|
|
16
|
+
single daemon-API call in Step 6. The runtime denies every other writer.
|
|
17
|
+
|
|
18
|
+
Available:
|
|
19
|
+
|
|
20
|
+
- `Read` / `Glob` / `Grep` — inspect the repository at `<localPath>` and any
|
|
21
|
+
documentation under it. Pass absolute paths; you do **not** need to `cd`.
|
|
22
|
+
- `Bash(curl *)` — pinned to `localhost:<apiPort>` by the security hook; used
|
|
23
|
+
exclusively for the Step 6 `PUT`.
|
|
24
|
+
- `Bash(jq *)` — JSON post-processing for the curl response.
|
|
25
|
+
|
|
26
|
+
Denied (these tools will fail at the SDK layer — do not attempt them):
|
|
27
|
+
|
|
28
|
+
- `Write`, `Edit` — no agent-side write to the worktree. The daemon owns
|
|
29
|
+
every byte under `git/<slug>/`.
|
|
30
|
+
- `Bash(git ...)`, `Bash(ls ...)`, `Bash(cat ...)` and any other shell verbs —
|
|
31
|
+
use `Glob` to enumerate directories and `Read` to inspect files. You are
|
|
32
|
+
analysing *current code structure*, not git history, so the git CLI is not
|
|
33
|
+
needed here. The daily-journal cron is the path that consumes git history.
|
|
34
|
+
|
|
13
35
|
## Inputs
|
|
14
36
|
|
|
15
37
|
Read `<task_context>` first. It contains:
|
|
@@ -52,7 +74,8 @@ synthesize.
|
|
|
52
74
|
1. **Read the README** at `<localPath>/README.md` (or `README.*`). Use
|
|
53
75
|
it as the author's stated framing of the project, but verify against
|
|
54
76
|
the code; the README can drift.
|
|
55
|
-
2. **Survey top-level structure.** `
|
|
77
|
+
2. **Survey top-level structure.** `Glob` `<localPath>/*` (and
|
|
78
|
+
`<localPath>/.*` for dotfiles you care about) + targeted `Read`s of
|
|
56
79
|
`package.json` / `pyproject.toml` / `Cargo.toml` / `go.mod` / etc. to
|
|
57
80
|
identify the language, build system, and entry points.
|
|
58
81
|
3. **Walk the meaningful directories.** Read enough source to confirm
|
|
@@ -75,6 +75,9 @@ same-backend delegated → your session's native Google Calendar MCP tools (no s
|
|
|
75
75
|
<!-- mode:delegated-cross:google_calendar -->
|
|
76
76
|
cross-backend delegated → `POST /api/integrations/google_calendar/exec` with a natural-language `task` + `outputSchema`, via the external-services skill (cross-backend variant materialized for this session). Do NOT call `/api/calendar/events` (returns 410) and do NOT fall back to your own backend's native Calendar MCP tools — they read a different Google account.
|
|
77
77
|
<!-- /mode:delegated-cross:google_calendar -->
|
|
78
|
+
<!-- mode:native:google_calendar -->
|
|
79
|
+
native → your session backend's native Google Calendar MCP tools (see the `external-services` skill's native body for the exact tool namespace per backend). `/api/calendar/*` returns 410 in native mode and `POST /api/integrations/google_calendar/exec` returns 410 with `X-Integration-Mode: native` — do NOT call either; the daemon does not proxy Calendar in native mode.
|
|
80
|
+
<!-- /mode:native:google_calendar -->
|
|
78
81
|
<!-- mode:disabled:google_calendar -->
|
|
79
82
|
disabled → tell the user real-time calendar access is unavailable in this configuration; work from the morning snapshot in <today> only.
|
|
80
83
|
<!-- /mode:disabled:google_calendar -->
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{context}
|
|
2
|
+
|
|
3
|
+
## DM Reply — Native Mode (Claude connectors)
|
|
4
|
+
|
|
5
|
+
Variant of `message.received.dm` selected when at least one integration
|
|
6
|
+
the agent may need to reach during this DM is in `native` mode and the
|
|
7
|
+
session backend (Claude) matches the integration's `nativeBackend`.
|
|
8
|
+
|
|
9
|
+
> **Connector routing (native).** `<integration_modes>` carries
|
|
10
|
+
> `gmail`, `google_calendar`, and `notion` mode attributes plus
|
|
11
|
+
> `<key>_native_backend="<backend>"` for every native key. The
|
|
12
|
+
> `<integration-routing-table>` block below is the audit table; the
|
|
13
|
+
> actionable per-integration routing table is what you act on. For any
|
|
14
|
+
> native integration the user's DM may need:
|
|
15
|
+
>
|
|
16
|
+
> - Call `mcp__claude_ai_<Service>__*` MCP tools directly. The exact
|
|
17
|
+
> tool namespace per service is documented in each skill's
|
|
18
|
+
> `SKILL.native.claude.md` body materialised for this session
|
|
19
|
+
> (`mail`, `external-services`, `notion`).
|
|
20
|
+
> - Do **NOT** call `POST /api/integrations/<key>/exec` — returns 410
|
|
21
|
+
> with `X-Integration-Mode: native`.
|
|
22
|
+
> - Do **NOT** call the per-integration daemon routes listed in
|
|
23
|
+
> `<integration-routing-table>` as `(DO NOT call /api/<key>/*)` —
|
|
24
|
+
> each route-prefix returns 410 in native mode.
|
|
25
|
+
> - Write-class MCP calls (per each connector's registry
|
|
26
|
+
> `destructiveTools` set) still require explicit user confirmation
|
|
27
|
+
> per `docs/design/09-safety-cost.md` §7. The absolute-block layer
|
|
28
|
+
> continues to fire regardless of mode.
|
|
29
|
+
> - For direct-mode siblings (e.g. when `gmail="native"` and
|
|
30
|
+
> `google_calendar="direct"`), keep using the direct routes
|
|
31
|
+
> documented in the base skill body for the direct integrations —
|
|
32
|
+
> the native gate is per-key.
|
|
33
|
+
|
|
34
|
+
### Per-session integration routing
|
|
35
|
+
|
|
36
|
+
<integration-routing-table>
|
|
37
|
+
|
|
38
|
+
### Native MCP routing — per integration
|
|
39
|
+
|
|
40
|
+
The Calendar / Mail / Notion entries below override the matching
|
|
41
|
+
sections in the shared base flow. Mode-conditional markers in the base
|
|
42
|
+
(`<!-- mode:native:google_calendar -->`) still fire, but the explicit
|
|
43
|
+
prose here carries the exact tool namespace and the safety contract
|
|
44
|
+
for Claude's hosted connectors so the agent does not have to derive it
|
|
45
|
+
mid-turn.
|
|
46
|
+
|
|
47
|
+
**Calendar — real-time queries.** When `google_calendar="native"`, use
|
|
48
|
+
the `mcp__claude_ai_Google_Calendar__*` MCP tools (`list_events` /
|
|
49
|
+
`get_event` / `list_calendars` / `suggest_time`). See the
|
|
50
|
+
`external-services` skill's native body for argument shapes and the
|
|
51
|
+
destructive-confirm contract for `create_event` / `update_event` /
|
|
52
|
+
`delete_event` / `respond_to_event`. `/api/calendar/*` returns 410.
|
|
53
|
+
`POST /api/integrations/google_calendar/exec` returns 410.
|
|
54
|
+
|
|
55
|
+
**Mail — DM read / draft / reply.** When `gmail="native"`, use the
|
|
56
|
+
`mcp__claude_ai_Gmail__*` MCP tools per the `mail` skill's native
|
|
57
|
+
body (`search_threads` / `get_thread` / `create_draft` / label
|
|
58
|
+
operations). Claude's hosted Gmail connector is **draft-only** —
|
|
59
|
+
there is no send / forward / delete surface. If the user explicitly
|
|
60
|
+
asks to send, point them at the Gmail web UI. For non-Gmail accounts
|
|
61
|
+
(IMAP / Outlook / iCloud / Yahoo), keep using the direct-mode
|
|
62
|
+
`/api/mail/<acct>/*` routes per the base `mail` skill body.
|
|
63
|
+
|
|
64
|
+
**Notion — DM read / search / page operations.** When `notion="native"`,
|
|
65
|
+
use the `mcp__claude_ai_Notion__*` MCP tools per the `notion` skill's
|
|
66
|
+
native body (`notion-search` / `notion-fetch` / read-class + the
|
|
67
|
+
destructive set). `/api/notion/databases` (label → UUID config dump)
|
|
68
|
+
is still reachable in every mode — consult it before any Notion
|
|
69
|
+
read so the MCP call carries a concrete UUID.
|
|
70
|
+
|
|
71
|
+
The dispatch decision flow (capture user info, profile-question
|
|
72
|
+
reconcile, compose reply, route durable intent) is identical to the
|
|
73
|
+
direct variant and lives below via the `{{> base }}` partial. Read
|
|
74
|
+
through it after the per-integration overrides above.
|
|
75
|
+
|
|
76
|
+
{{> base }}
|