@aitne-sh/aitne 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +151 -147
  2. package/agent-assets/agent-profiles/_safety.md +14 -0
  3. package/agent-assets/agent-profiles/conversational.md +23 -1
  4. package/agent-assets/agent-profiles/observer.md +15 -0
  5. package/agent-assets/agent-profiles/routine-fetch-window.md +128 -0
  6. package/agent-assets/agent-profiles/routine.md +16 -0
  7. package/agent-assets/agent-profiles/task.md +15 -0
  8. package/agent-assets/docs/concepts/auth-health.md +25 -9
  9. package/agent-assets/docs/concepts/backends-and-tiers.md +40 -4
  10. package/agent-assets/docs/concepts/costs-and-quotas.md +87 -25
  11. package/agent-assets/docs/concepts/delegated-mode.md +7 -13
  12. package/agent-assets/docs/concepts/memory-model.md +14 -1
  13. package/agent-assets/docs/concepts/observations.md +19 -1
  14. package/agent-assets/docs/concepts/process-keys.md +5 -0
  15. package/agent-assets/docs/concepts/routines.md +22 -10
  16. package/agent-assets/docs/concepts/safety-model.md +3 -8
  17. package/agent-assets/docs/concepts/skills.md +36 -1
  18. package/agent-assets/docs/features/integrations/calendar.md +74 -3
  19. package/agent-assets/docs/features/integrations/git.md +4 -4
  20. package/agent-assets/docs/features/integrations/github.md +75 -107
  21. package/agent-assets/docs/features/lifestyle/git.md +169 -22
  22. package/agent-assets/docs/features/messaging/overview.md +10 -1
  23. package/agent-assets/docs/features/routines/morning-routine.md +1 -1
  24. package/agent-assets/docs/getting-started/01-what-is-this.md +30 -12
  25. package/agent-assets/docs/getting-started/02-first-steps.md +15 -4
  26. package/agent-assets/docs/getting-started/03-what-can-this-do.md +17 -2
  27. package/agent-assets/docs/guides/install-and-run.md +10 -1
  28. package/agent-assets/docs/guides/setup-wizard.md +43 -6
  29. package/agent-assets/docs/guides/switch-default-backend.md +7 -3
  30. package/agent-assets/docs/reference/skills.md +10 -1
  31. package/agent-assets/docs/troubleshooting/auth-failed.md +27 -8
  32. package/agent-assets/docs/troubleshooting/quota-exhausted.md +35 -12
  33. package/agent-assets/skills/context/SKILL.md +6 -0
  34. package/agent-assets/skills/external-services/SKILL.md +4 -0
  35. package/agent-assets/skills/external-services/SKILL.native.claude.md +320 -0
  36. package/agent-assets/skills/external-services/SKILL.native.codex.md +243 -0
  37. package/agent-assets/skills/external-services/SKILL.native.gemini.md +237 -0
  38. package/agent-assets/skills/mail/SKILL.md +42 -14
  39. package/agent-assets/skills/mail/SKILL.native.claude.md +175 -0
  40. package/agent-assets/skills/mail/SKILL.native.codex.md +165 -0
  41. package/agent-assets/skills/mail/SKILL.native.gemini.md +169 -0
  42. package/agent-assets/skills/management-task-modify/SKILL.md +2 -1
  43. package/agent-assets/skills/management-task-stop/SKILL.md +2 -2
  44. package/agent-assets/skills/notify/SKILL.md +4 -4
  45. package/agent-assets/skills/notion/SKILL.md +6 -0
  46. package/agent-assets/skills/notion/SKILL.native.claude.md +202 -0
  47. package/agent-assets/skills/notion/SKILL.native.codex.md +166 -0
  48. package/agent-assets/skills/notion/SKILL.native.gemini.md +167 -0
  49. package/agent-assets/skills/observations/SKILL.md +29 -4
  50. package/agent-assets/skills/project-doc/SKILL.md +6 -0
  51. package/agent-assets/skills/reading/SKILL.md +2 -0
  52. package/agent-assets/skills/roadmap/SKILL.md +19 -4
  53. package/agent-assets/skills/today/SKILL.md +32 -6
  54. package/agent-assets/skills/user-interview/SKILL.md +1 -1
  55. package/agent-assets/skills/user-profile/SKILL.md +7 -0
  56. package/agent-assets/task-flows/_partials/calendar-acquire.google_calendar.md +119 -0
  57. package/agent-assets/task-flows/_partials/calendar-acquire.outlook_calendar.md +101 -0
  58. package/agent-assets/task-flows/_partials/mail-acquire.gmail.md +113 -0
  59. package/agent-assets/task-flows/_partials/mail-acquire.outlook_mail.md +97 -0
  60. package/agent-assets/task-flows/_partials/notion-acquire.notion.md +104 -0
  61. package/agent-assets/task-flows/git.project.refresh_architecture.md +24 -1
  62. package/agent-assets/task-flows/message.received.dm.md +3 -0
  63. package/agent-assets/task-flows/message.received.dm.native.claude.md +76 -0
  64. package/agent-assets/task-flows/message.received.dm.native.codex.md +57 -0
  65. package/agent-assets/task-flows/message.received.dm.native.gemini.md +70 -0
  66. package/agent-assets/task-flows/message.received.dm_first.md +3 -0
  67. package/agent-assets/task-flows/message.received.dm_first.native.claude.md +56 -0
  68. package/agent-assets/task-flows/message.received.dm_first.native.codex.md +48 -0
  69. package/agent-assets/task-flows/message.received.dm_first.native.gemini.md +54 -0
  70. package/agent-assets/task-flows/routine.evening_review.md +28 -1
  71. package/agent-assets/task-flows/routine.fetch_window.md +93 -0
  72. package/agent-assets/task-flows/routine.hourly_check.md +44 -5
  73. package/agent-assets/task-flows/routine.monthly_review.md +13 -2
  74. package/agent-assets/task-flows/routine.morning_routine.md +55 -42
  75. package/agent-assets/task-flows/routine.morning_routine_initial.md +37 -38
  76. package/agent-assets/task-flows/routine.roadmap_refresh.md +45 -49
  77. package/agent-assets/task-flows/routine.today_refresh.md +53 -96
  78. package/agent-assets/task-flows/routine.weekly_review.md +40 -17
  79. package/agent-assets/task-flows/scheduled.dm.md +13 -11
  80. package/agent-assets/task-flows/scheduled.task.md +2 -2
  81. package/agent-assets/task-flows/setup.initial.md +53 -25
  82. package/agent-assets/task-flows/setup.update.md +1 -1
  83. package/agent-assets/templates/README.md +13 -6
  84. package/package.json +47 -23
  85. package/agent-assets/task-flows/routine.hourly_check.delegated.claude.md +0 -405
  86. package/agent-assets/task-flows/routine.hourly_check.delegated.codex.md +0 -400
  87. package/agent-assets/task-flows/routine.hourly_check.delegated.gemini.md +0 -404
  88. package/scripts/check-redaction-coverage.mjs +0 -109
  89. package/scripts/commands.md +0 -0
  90. package/scripts/message-discipline-digest.mjs +0 -535
  91. package/scripts/poc/google-connector-inheritance/REPORT.md +0 -197
  92. package/scripts/poc/google-connector-inheritance/claude-sdk-probe.mjs +0 -79
  93. package/scripts/remint-roadmap-ids.mjs +0 -257
  94. package/scripts/smoke-obsidian-api.mjs +0 -166
@@ -8,6 +8,13 @@ allowed-tools:
8
8
 
9
9
  # today.md Guide
10
10
 
11
+ Output language: follow `<output_language_policy>`. today.md is
12
+ Policy B — the six required H2 headers (`## User Schedule`,
13
+ `## User Tasks`, `## Agent Plan`, `## Agent Notes`, `## Agent Log`,
14
+ `## Handoff`) are skeleton and stay English; bullets, narrative, and
15
+ free-text fields under them are written in `<settings primary_language>`.
16
+ Preserve user-customized headers verbatim.
17
+
11
18
  today.md has a **day-type header line** (second line) and **six required sections**.
12
19
 
13
20
  ```
@@ -179,15 +186,34 @@ PUT today.md must contain the H1 date line, day-type header quote, and all six s
179
186
 
180
187
  ## today.md API (subset of context API)
181
188
 
189
+ Body submission follows the rule in `_safety.md` "Daemon-API body
190
+ submission": small section PATCHes use inline `-d '{...}'`; full-file
191
+ PUT uses the stdin heredoc `-d @- <<'JSON'` shape because the body
192
+ runs multi-KB. Add `X-Lock-Id: <today_write_lock_id>` on every
193
+ PUT / PATCH when that tag is in your context.
194
+
182
195
  ```bash
183
- curl -s http://localhost:8321/api/context/today # Read
184
- curl -s -X PUT http://localhost:8321/api/context/today \ # Full replace (Morning only)
185
- -H 'Content-Type: application/json' -d '{"content": "# 2026-04-02 (Thu)\n..."}'
186
- curl -s -X PATCH http://localhost:8321/api/context/today \ # Section operation
187
- -H 'Content-Type: application/json' -d '{"section": "agent_log", "mode": "append", "content": "- 09:35 ..."}'
196
+ # Read
197
+ curl -s http://localhost:8321/api/context/today
198
+
199
+ # Full replace Morning Routine only. Multi-KB body → heredoc.
200
+ curl -s -X PUT http://localhost:8321/api/context/today \
201
+ -H 'Content-Type: application/json' \
202
+ -H 'X-Lock-Id: <today_write_lock_id>' \
203
+ -d @- <<'JSON'
204
+ {"content":"# 2026-04-02 (Thursday)\n> Day type: Weekday | Work focus: on | Study focus: on | Personal focus: on\n\n## User Schedule\n- 14:00–15:00 Design review [work]\n\n## User Tasks\n- [ ] 11:00 Finalize Q2 draft [work]\n\n## Agent Plan\n- [ ] 08:55 DM reminder: standup [work] →DM\n\n## Agent Notes\n\n## Agent Log\n- 04:00 Morning Routine completed (day-type: Weekday)\n\n## Handoff\n- (none)\n"}
205
+ JSON
206
+
207
+ # Section operation — small body, inline `-d` is fine.
208
+ curl -s -X PATCH http://localhost:8321/api/context/today \
209
+ -H 'Content-Type: application/json' \
210
+ -H 'X-Lock-Id: <today_write_lock_id>' \
211
+ -d '{"section":"agent_log","mode":"append","content":"- 09:35 ..."}'
188
212
  ```
189
213
 
190
- Add `X-Lock-Id` header if `<today_write_lock_id>` is in context. See context skill for full API reference.
214
+ The H1 date and the `> Day type:` quote line are validated by exact
215
+ regex on PUT — keep both English-shaped exactly as the template. See
216
+ context skill for the full endpoint reference.
191
217
 
192
218
  ## Knowledge map — section shape (auto-curated)
193
219
 
@@ -37,7 +37,7 @@ curl -s http://localhost:8321/api/context/agent/profile-questions
37
37
  | `target_path` | which user file the answer should land in, e.g. `user/profile.md` |
38
38
  | `## Section` | optional — narrows to a section within the target file |
39
39
  | `match=<anchor>` | optional — bullet key (English, like `Name`, `Timezone`, `Sleep`, `Working hours`). Required when multiple rows share a section, or when setup pre-seeds the section with an unrelated bullet |
40
- | `ask-hint` | English brief of WHAT to ask. Render the actual DM in the user's `primary_language` at delivery time |
40
+ | `ask-hint` | English brief of WHAT to ask (agent-internal, Policy A). Render the actual DM per `<output_language_policy>` this skill intentionally splits the two surfaces |
41
41
  | `last_attempted=...` | optional inline HTML comment maintained by the evening sweep — selector deprioritises rows whose comment is < 7 days old |
42
42
 
43
43
  ### In Progress entry format
@@ -8,6 +8,13 @@ allowed-tools:
8
8
 
9
9
  # User Profile Update Guide
10
10
 
11
+ Output language: follow `<output_language_policy>`. `user/profile.md`
12
+ and `user/*.md` are Policy B — template H2 headers (`## Identity`,
13
+ `## Work Pattern`, `## Platforms`, `## Expertise`, `## Notification
14
+ Preferences`, `## Learned Context`, `## Raw Signals`) are skeleton and
15
+ stay English; the facts you write under them are in
16
+ `<settings primary_language>`. Preserve user-customized headers verbatim.
17
+
11
18
  `user/profile.md` stores the user's identity, preferences, and learned behavioral patterns. It is injected into every agent session via `<user>` tags — keep it concise (target: under ~600 tokens total).
12
19
 
13
20
  Detailed, dictionary-like background belongs under `user/*.md`. Read `user/_index.md` first, then fetch only the topic file you need.
@@ -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.** `ls <localPath>` + targeted reads of
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