@aitne-sh/aitne 0.1.9 → 0.1.11

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 (218) hide show
  1. package/README.md +41 -11
  2. package/agent-assets/agent-profiles/background-task.md +53 -0
  3. package/agent-assets/agent-profiles/conversational.md +1 -0
  4. package/agent-assets/agent-profiles/routine-fetch-window.md +14 -75
  5. package/agent-assets/agent-profiles/routine.md +1 -1
  6. package/agent-assets/agents/{hourly-check → activity-scan}/agent.md +22 -17
  7. package/agent-assets/agents/monthly-review/agent.md +6 -5
  8. package/agent-assets/docs/concepts/agent-day.md +6 -7
  9. package/agent-assets/docs/concepts/auth-health.md +23 -20
  10. package/agent-assets/docs/concepts/backends-and-tiers.md +13 -9
  11. package/agent-assets/docs/concepts/costs-and-quotas.md +14 -12
  12. package/agent-assets/docs/concepts/delegated-mode.md +18 -17
  13. package/agent-assets/docs/concepts/memory-model.md +16 -9
  14. package/agent-assets/docs/concepts/observations.md +24 -20
  15. package/agent-assets/docs/concepts/process-keys.md +10 -9
  16. package/agent-assets/docs/concepts/routines.md +34 -31
  17. package/agent-assets/docs/concepts/safety-and-execution.md +11 -7
  18. package/agent-assets/docs/concepts/safety-model.md +39 -25
  19. package/agent-assets/docs/concepts/skills.md +12 -10
  20. package/agent-assets/docs/features/integrations/browser-history.md +23 -18
  21. package/agent-assets/docs/features/integrations/calendar.md +28 -17
  22. package/agent-assets/docs/features/integrations/git.md +13 -11
  23. package/agent-assets/docs/features/integrations/github.md +22 -14
  24. package/agent-assets/docs/features/integrations/mail.md +25 -22
  25. package/agent-assets/docs/features/integrations/notion.md +35 -11
  26. package/agent-assets/docs/features/integrations/obsidian.md +8 -8
  27. package/agent-assets/docs/features/lifestyle/git.md +27 -23
  28. package/agent-assets/docs/features/lifestyle/reading.md +20 -11
  29. package/agent-assets/docs/features/lifestyle/receipts.md +11 -10
  30. package/agent-assets/docs/features/lifestyle/travel-bookings.md +4 -3
  31. package/agent-assets/docs/features/memory-files/agent-journal.md +53 -26
  32. package/agent-assets/docs/features/memory-files/agent-lessons.md +178 -0
  33. package/agent-assets/docs/features/memory-files/projects.md +11 -8
  34. package/agent-assets/docs/features/memory-files/roadmap.md +17 -14
  35. package/agent-assets/docs/features/memory-files/schedule.md +6 -3
  36. package/agent-assets/docs/features/memory-files/today.md +10 -7
  37. package/agent-assets/docs/features/memory-files/user-profile.md +14 -9
  38. package/agent-assets/docs/features/messaging/bang-commands.md +21 -6
  39. package/agent-assets/docs/features/messaging/overview.md +17 -14
  40. package/agent-assets/docs/features/messaging/telegram.md +10 -9
  41. package/agent-assets/docs/features/operations/activity-and-conversations.md +6 -5
  42. package/agent-assets/docs/features/operations/approvals.md +6 -5
  43. package/agent-assets/docs/features/operations/browser-tasks.md +184 -0
  44. package/agent-assets/docs/features/operations/cost-tracking.md +11 -1
  45. package/agent-assets/docs/features/operations/managed-chromium.md +4 -2
  46. package/agent-assets/docs/features/operations/notifications.md +11 -1
  47. package/agent-assets/docs/features/operations/quiet-hours.md +41 -11
  48. package/agent-assets/docs/features/operations/schedule-approaching.md +2 -2
  49. package/agent-assets/docs/features/routines/activity-scan.md +220 -0
  50. package/agent-assets/docs/features/routines/custom-routines.md +82 -134
  51. package/agent-assets/docs/features/routines/evening-review.md +23 -13
  52. package/agent-assets/docs/features/routines/morning-routine.md +7 -5
  53. package/agent-assets/docs/features/routines/weekly-review.md +24 -3
  54. package/agent-assets/docs/features/wiki/commands.md +4 -4
  55. package/agent-assets/docs/features/wiki/cost-and-approval.md +4 -3
  56. package/agent-assets/docs/features/wiki/dashboard.md +7 -6
  57. package/agent-assets/docs/features/wiki/overview.md +3 -3
  58. package/agent-assets/docs/features/wiki/search.md +5 -5
  59. package/agent-assets/docs/features/wiki/workspaces.md +2 -2
  60. package/agent-assets/docs/getting-started/01-what-is-this.md +3 -3
  61. package/agent-assets/docs/getting-started/02-first-steps.md +5 -3
  62. package/agent-assets/docs/getting-started/04-first-day.md +27 -30
  63. package/agent-assets/docs/glossary.md +8 -8
  64. package/agent-assets/docs/guides/add-a-custom-routine.md +122 -68
  65. package/agent-assets/docs/guides/budget-and-cost-for-wiki.md +2 -2
  66. package/agent-assets/docs/guides/connect-a-new-mail-account.md +4 -2
  67. package/agent-assets/docs/guides/explore-with-trace-and-connect.md +5 -4
  68. package/agent-assets/docs/guides/install-and-run.md +2 -2
  69. package/agent-assets/docs/guides/maintain-wiki-health.md +2 -2
  70. package/agent-assets/docs/guides/pause-the-agent.md +27 -21
  71. package/agent-assets/docs/guides/reinstall-cleanly.md +2 -0
  72. package/agent-assets/docs/guides/setup-wizard.md +12 -6
  73. package/agent-assets/docs/guides/use-an-existing-obsidian-vault.md +6 -6
  74. package/agent-assets/docs/reference/api.md +26 -5
  75. package/agent-assets/docs/reference/cli-commands.md +3 -3
  76. package/agent-assets/docs/reference/config.md +51 -24
  77. package/agent-assets/docs/reference/disallowed-tools.md +6 -4
  78. package/agent-assets/docs/reference/keyboard-shortcuts.md +2 -2
  79. package/agent-assets/docs/reference/knowledge-layout.md +25 -12
  80. package/agent-assets/docs/reference/process-keys.md +9 -9
  81. package/agent-assets/docs/reference/skills.md +10 -6
  82. package/agent-assets/docs/troubleshooting/auth-failed.md +9 -8
  83. package/agent-assets/docs/troubleshooting/dashboard-shows-degraded.md +16 -9
  84. package/agent-assets/docs/troubleshooting/messaging-not-pairing.md +2 -2
  85. package/agent-assets/docs/troubleshooting/morning-routine-didnt-run.md +32 -16
  86. package/agent-assets/docs/troubleshooting/observation-not-detected.md +26 -24
  87. package/agent-assets/docs/troubleshooting/quota-exhausted.md +7 -6
  88. package/agent-assets/docs/troubleshooting/wiki-write-failed.md +3 -3
  89. package/agent-assets/skills/agent-actions/SKILL.md +23 -39
  90. package/agent-assets/skills/agent-create/SKILL.md +26 -6
  91. package/agent-assets/skills/attach/SKILL.md +8 -27
  92. package/agent-assets/skills/background-task/SKILL.md +184 -0
  93. package/agent-assets/skills/background-task-reply/SKILL.md +100 -0
  94. package/agent-assets/skills/browser-history/SKILL.md +60 -29
  95. package/agent-assets/skills/browser-history-respond/SKILL.md +6 -1
  96. package/agent-assets/skills/browser-task/SKILL.md +33 -31
  97. package/agent-assets/skills/context/SKILL.md +26 -34
  98. package/agent-assets/skills/context/curation.json +12 -12
  99. package/agent-assets/skills/context/references/api.md +22 -20
  100. package/agent-assets/skills/context/references/required-frontmatter.md +10 -9
  101. package/agent-assets/skills/context/references/snapshot-files.md +16 -15
  102. package/agent-assets/skills/context/seeds/file-responsibilities.seed.json +5 -5
  103. package/agent-assets/skills/context/seeds/frontmatter-requirements.seed.json +3 -3
  104. package/agent-assets/skills/docs-search/SKILL.md +19 -31
  105. package/agent-assets/skills/external-services/SKILL.delegated.claude.md +8 -95
  106. package/agent-assets/skills/external-services/SKILL.delegated.codex.md +8 -94
  107. package/agent-assets/skills/external-services/SKILL.delegated.gemini.md +8 -94
  108. package/agent-assets/skills/external-services/SKILL.native.claude.md +15 -9
  109. package/agent-assets/skills/external-services/SKILL.native.codex.md +11 -5
  110. package/agent-assets/skills/external-services/SKILL.native.gemini.md +11 -5
  111. package/agent-assets/skills/external-services/references/exec-errors.md +32 -0
  112. package/agent-assets/skills/external-services/references/skills-crud.md +5 -5
  113. package/agent-assets/skills/gmail-lifestyle/SKILL.md +3 -2
  114. package/agent-assets/skills/gmail-lifestyle/references/receipts-api.md +4 -0
  115. package/agent-assets/skills/gmail-lifestyle/references/travel-bookings-api.md +9 -0
  116. package/agent-assets/skills/mail/SKILL.delegated.claude.md +13 -25
  117. package/agent-assets/skills/mail/SKILL.delegated.codex.md +3 -2
  118. package/agent-assets/skills/mail/SKILL.delegated.gemini.md +3 -2
  119. package/agent-assets/skills/mail/SKILL.md +12 -20
  120. package/agent-assets/skills/mail/SKILL.native.claude.md +24 -16
  121. package/agent-assets/skills/mail/SKILL.native.codex.md +16 -9
  122. package/agent-assets/skills/mail/SKILL.native.gemini.md +12 -6
  123. package/agent-assets/skills/mail/references/api.md +6 -1
  124. package/agent-assets/skills/mail/references/examples.md +2 -1
  125. package/agent-assets/skills/managed-tasks/SKILL.md +44 -77
  126. package/agent-assets/skills/managed-tasks/references/errors.md +25 -14
  127. package/agent-assets/skills/managed-tasks/references/output-path.md +33 -17
  128. package/agent-assets/skills/managed-tasks/references/recurrence-rule.md +26 -16
  129. package/agent-assets/skills/management-policy/SKILL.md +36 -28
  130. package/agent-assets/skills/management-policy/curation.json +1 -1
  131. package/agent-assets/skills/management-policy/references/policy-workflow.md +30 -18
  132. package/agent-assets/skills/notify/SKILL.md +16 -13
  133. package/agent-assets/skills/notify/references/priority.md +42 -26
  134. package/agent-assets/skills/notion/SKILL.delegated.claude.md +1 -1
  135. package/agent-assets/skills/notion/SKILL.delegated.codex.md +1 -1
  136. package/agent-assets/skills/notion/SKILL.delegated.gemini.md +1 -1
  137. package/agent-assets/skills/notion/SKILL.md +18 -18
  138. package/agent-assets/skills/notion/SKILL.native.claude.md +4 -4
  139. package/agent-assets/skills/notion/SKILL.native.codex.md +3 -3
  140. package/agent-assets/skills/notion/SKILL.native.gemini.md +3 -3
  141. package/agent-assets/skills/observations/SKILL.md +9 -24
  142. package/agent-assets/skills/observations/references/fetch-fallback.md +22 -0
  143. package/agent-assets/skills/project-doc/SKILL.md +9 -6
  144. package/agent-assets/skills/project-doc/curation.json +3 -3
  145. package/agent-assets/skills/project-doc/seeds/project-shape.seed.json +2 -2
  146. package/agent-assets/skills/project-doc/seeds/slug-grammar.seed.json +3 -3
  147. package/agent-assets/skills/reading/SKILL.md +8 -42
  148. package/agent-assets/skills/reading/references/reading-taste.md +5 -5
  149. package/agent-assets/skills/roadmap/SKILL.md +3 -19
  150. package/agent-assets/skills/roadmap/references/api.md +23 -8
  151. package/agent-assets/skills/roadmap/references/horizon-tags.md +11 -0
  152. package/agent-assets/skills/roadmap/references/migration.md +8 -6
  153. package/agent-assets/skills/roadmap/references/retention.md +18 -0
  154. package/agent-assets/skills/schedule/SKILL.md +20 -28
  155. package/agent-assets/skills/schedule/references/importance.md +23 -0
  156. package/agent-assets/skills/schedule/references/recurrence-rule.md +26 -16
  157. package/agent-assets/skills/scheduled-managed-task/SKILL.md +46 -46
  158. package/agent-assets/skills/today/SKILL.md +38 -81
  159. package/agent-assets/skills/today/references/agent-plan-lifecycle.md +9 -4
  160. package/agent-assets/skills/today/references/agent-plan-revision.md +28 -0
  161. package/agent-assets/skills/today/references/today-skeleton.md +66 -0
  162. package/agent-assets/skills/today/seeds/agent-notes-flavors.seed.json +1 -1
  163. package/agent-assets/skills/today/seeds/section-shape.seed.json +6 -6
  164. package/agent-assets/skills/user-interview/SKILL.md +15 -90
  165. package/agent-assets/skills/user-interview/references/op-briefing.md +1 -1
  166. package/agent-assets/skills/user-interview/references/op-dm-handler.md +88 -0
  167. package/agent-assets/skills/user-interview/references/op-morning.md +2 -2
  168. package/agent-assets/skills/user-interview/references/sweep-and-fallback.md +1 -1
  169. package/agent-assets/skills/user-profile/SKILL.md +16 -26
  170. package/agent-assets/skills/user-profile/curation.json +3 -3
  171. package/agent-assets/skills/user-profile/references/character-preferences.md +3 -3
  172. package/agent-assets/skills/wiki/wiki-ask/SKILL.md +1 -1
  173. package/agent-assets/skills/wiki/wiki-compile/SKILL.md +5 -4
  174. package/agent-assets/skills/wiki/wiki-connect/SKILL.md +32 -5
  175. package/agent-assets/skills/wiki/wiki-ingest/SKILL.md +6 -50
  176. package/agent-assets/skills/wiki/wiki-ingest/references/curl-errors.md +58 -0
  177. package/agent-assets/skills/wiki/wiki-lint/SKILL.md +20 -14
  178. package/agent-assets/skills/wiki/wiki-trace/SKILL.md +10 -5
  179. package/agent-assets/skills/wiki/wiki-vault-rules/SKILL.md +2 -0
  180. package/agent-assets/system-prompts/routine-research-cluster-update.md +71 -0
  181. package/agent-assets/task-flows/_partials/feedback-capture.md +30 -0
  182. package/agent-assets/task-flows/_partials/notion-acquire.notion.md +47 -21
  183. package/agent-assets/task-flows/background_task.md +81 -0
  184. package/agent-assets/task-flows/git.local_ahead.stale.md +1 -1
  185. package/agent-assets/task-flows/git.push.detected.md +1 -1
  186. package/agent-assets/task-flows/git.tag.created.md +1 -1
  187. package/agent-assets/task-flows/github.assigned.md +1 -1
  188. package/agent-assets/task-flows/github.pull_request.review_requested.md +2 -2
  189. package/agent-assets/task-flows/github.security_alert.md +1 -1
  190. package/agent-assets/task-flows/message.received.dm.md +11 -3
  191. package/agent-assets/task-flows/message.received.dm_first.md +8 -2
  192. package/agent-assets/task-flows/{routine.hourly_check.md → routine.activity_scan.md} +31 -23
  193. package/agent-assets/task-flows/{routine.hourly_check.triage.md → routine.activity_scan.triage.md} +3 -3
  194. package/agent-assets/task-flows/routine.evening_review.md +80 -0
  195. package/agent-assets/task-flows/routine.monthly_review.md +81 -8
  196. package/agent-assets/task-flows/routine.research_cluster_update.md +33 -19
  197. package/agent-assets/task-flows/routine.roadmap_refresh.md +2 -2
  198. package/agent-assets/task-flows/routine.today_refresh.md +1 -1
  199. package/agent-assets/task-flows/routine.weekly_review.md +124 -4
  200. package/agent-assets/task-flows/schedule.approaching.md +2 -2
  201. package/agent-assets/task-flows/scheduled.dm.md +77 -1
  202. package/agent-assets/task-flows/scheduled.task.md +7 -1
  203. package/agent-assets/task-flows/wiki.trace.md +1 -1
  204. package/agent-assets/templates/_manifest.json +2 -2
  205. package/agent-assets/templates/knowledge/dossiers/_index.md +1 -1
  206. package/agent-assets/templates/knowledge/dossiers/{hourly.md → activity-scan.md} +1 -1
  207. package/agent-assets/templates/policies/journal-format.md +1 -1
  208. package/agent-assets/templates/policies/mcp.md +1 -1
  209. package/agent-assets/templates/policies/routines/_index.md +1 -1
  210. package/agent-assets/templates/policies/routines/{hourly.md → activity-scan.md} +5 -5
  211. package/bin/aitne.mjs +45 -11
  212. package/package.json +6 -5
  213. package/scripts/commands/doctor.mjs +11 -2
  214. package/scripts/lib/process-identity.d.mts +46 -0
  215. package/scripts/lib/process-identity.mjs +193 -0
  216. package/scripts/lib/read-api-token.mjs +1 -1
  217. package/scripts/start.mjs +14 -4
  218. package/agent-assets/docs/features/routines/hourly-check.md +0 -205
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: user-interview
3
- description: Read or modify the profile-interview queue at agent/profile-questions.md. Pick a question (latent), weave a latent question into a natural reply, tick when the user answers, fallback-promote stale rows, or reconcile against user/*.md. Never sends cold standalone DMs.
3
+ description: Use during morning routine Step 7.5, DM replies, the morning briefing, and the evening sweep to manage the profile-interview queue at state/profile-questions.md pick a latent question, weave it into a natural reply, tick when answered, fallback-promote stale rows.
4
4
  allowed-tools:
5
5
  - Bash(curl *)
6
6
  - Read
@@ -94,7 +94,7 @@ Before scheduling or asking, every callsite SHOULD verify the target
94
94
  slot is genuinely empty:
95
95
 
96
96
  ```bash
97
- curl -s "http://localhost:8321/api/profile-questions/slot-filled?path=user/profile.md&section=Identity&anchor=Name"
97
+ curl -s "http://localhost:8321/api/profile-questions/slot-filled?path=identity/profile.md&section=Identity&anchor=Name"
98
98
  # → {"filled":true|false,"sectionPresent":true|false,"fileExists":true|false,...}
99
99
  ```
100
100
 
@@ -109,59 +109,16 @@ the op-morning reference below.
109
109
 
110
110
  {{> ref:op-morning }}
111
111
 
112
- ## Operation 2 — Latent opportunity check (DM handler)
112
+ ## Operations 2 & 4 DM-handler weave and answer capture
113
113
 
114
- Run this AFTER the standard "Capture user info" block in
115
- `message.received.dm.md` / `message.received.dm_first.md`, BEFORE
116
- composing the reply.
114
+ DM-handler-only. Operation 2 (latent opportunity check) runs AFTER the
115
+ standard "Capture user info" block in `message.received.dm.md` /
116
+ `message.received.dm_first.md`, BEFORE composing the reply; Operation 4
117
+ (answer capture) runs after Operation 2's opportunity check. The
118
+ slot-filled pre-check, naturalness rules, two PATCHes, and the "one DM =
119
+ one tick" rationale are in the op-dm-handler reference below.
117
120
 
118
- ```
119
- 1. GET agent/profile-questions.md ## In Progress.
120
- 2. If no entry has state=latent, skip — return to the normal reply path.
121
- 2.5 Slot-filled pre-check (MANDATORY before any weaving decision).
122
- GET /api/profile-questions/slot-filled?path=<target>&section=<section?>&anchor=<anchor?>
123
- for the latent row's target. If `filled: true`, the slot was filled
124
- between morning routine and now (the user may have volunteered the
125
- answer in a previous DM, or the sweep that runs at 17:50 hasn't
126
- caught up yet). Resolve the row instead of weaving:
127
- - Tick the matching ## Pending row [ ] → [x] (read-rebuild + replace).
128
- - Remove the entry from ## In Progress.
129
- - Append `- [x] <today> → <id> (reconciled:opportunity)` to ## Answered.
130
- - Remove the matching `Profile question (latent): <id>` line from
131
- today.md ## Agent Notes.
132
- Return to the normal reply path — DO NOT weave a question. The user
133
- has effectively already answered it.
134
- 3. Otherwise (slot still empty), judge whether the inbound DM is a
135
- natural moment to ask the question. The criteria:
136
- - Topic match. The user's message touches the question's domain
137
- (work questions when the user mentions a meeting, code, deadline;
138
- personal questions when the user discusses lifestyle, hobbies,
139
- weekend plans; identity / location questions when the user makes
140
- a general greeting on day 1 or asks for time/weather).
141
- - Length appropriateness. Your reply will be more than a one-liner;
142
- there is room to weave a question without making the message look
143
- forced.
144
- - Mood appropriateness. The user is not venting / in crisis / asking
145
- a single short factual question.
146
- 4. If yes:
147
- a. Compose the reply with the question woven in NATURALLY. Do NOT
148
- make a separate paragraph or "By the way, …" preamble. The
149
- question should feel like a side comment, not an interview cue.
150
- b. After the reply is ready (final assistant text already includes
151
- the question), issue **two PATCH replaces** (separate calls —
152
- they target different files):
153
- - `PATCH /api/context/state/profile-questions` section=in_progress
154
- — flip the entry to
155
- `state=asked :: since=<unchanged> :: asked_at=<current_time>`
156
- (preserve the original `since=` date).
157
- - `PATCH /api/context/state/today` section=agent_notes — replace the
158
- parenthetical from `(latent)` to `(asked HH:MM)` and append
159
- `(asked HH:MM)` to the same line; preserve every other Agent
160
- Notes line byte-for-byte.
161
- 5. If no, leave the latent entry untouched. Return to the normal reply
162
- path. The opportunity will come (or the evening sweep will eventually
163
- promote to a fallback scheduled DM if it doesn't).
164
- ```
121
+ {{> ref:op-dm-handler }}
165
122
 
166
123
  ## Operation 3 — Latent piggyback (morning briefing composition)
167
124
 
@@ -172,35 +129,6 @@ op-briefing reference below.
172
129
 
173
130
  {{> ref:op-briefing }}
174
131
 
175
- ## Operation 4 — Capture the answer (DM handler queue reconcile)
176
-
177
- Run this AFTER "Capture user info" and Operation 2's opportunity check,
178
- BEFORE composing the reply text.
179
-
180
- ```
181
- 1. GET agent/profile-questions.md ## In Progress.
182
- 2. If an entry has state=asked AND (now − asked_at) < 24h:
183
- a. Tick the matching ## Pending row [ ] → [x] via PATCH replace
184
- (read-rebuild full Pending body, change just that one line).
185
- b. Remove the entry from ## In Progress (PATCH replace).
186
- c. Append `- [x] <today> → <id> (DM)` to ## Answered.
187
- d. Remove the matching today.md ## Agent Notes line — match by
188
- the `Profile question (` prefix and the `: <id>` id segment;
189
- works for both `(asked HH:MM)` and `(latent)` parenthetical
190
- states (PATCH replace, read-rebuild — preserve every other line
191
- byte-for-byte). Without this the line lingers in today.md
192
- until tomorrow's PUT-replace and confuses the user.
193
- 3. If state=latent or state=scheduled, leave the entry alone — this
194
- inbound DM is by definition unrelated to a question the user has
195
- not been asked.
196
- ```
197
-
198
- The "one DM after fire = one tick" rule is intentional. Trying to detect
199
- "did the user ACTUALLY answer?" is unreliable and would re-ask forever
200
- on partial answers. The 7-day `last_attempted` cooldown means an
201
- unresolved fact will surface again later through the natural-volunteering
202
- path or the next opportunity.
203
-
204
132
  {{> ref:sweep-and-fallback }}
205
133
 
206
134
  ## Today.md surfacing — Agent Notes flavor
@@ -258,20 +186,17 @@ those bullets and the system degrades to Layer-4-only protection
258
186
  curl -s http://localhost:8321/api/context/state/profile-questions
259
187
 
260
188
  # Slot-filled probe
261
- curl -s "http://localhost:8321/api/profile-questions/slot-filled?path=user/profile.md&section=Identity&anchor=Name"
189
+ curl -s "http://localhost:8321/api/profile-questions/slot-filled?path=identity/profile.md&section=Identity&anchor=Name"
262
190
 
263
191
  # Section-level edit (queue file uses the standard context API)
264
192
  curl -s -X PATCH http://localhost:8321/api/context/state/profile-questions \
265
193
  -H 'Content-Type: application/json' \
266
194
  -d '{"section": "in_progress", "mode": "replace", "content": "- name :: state=latent"}'
267
195
 
268
- # Fallback DM scheduling (Operation 5B only)
269
- # `prompt` is REQUIRED (the wake-up session has NO memory — it is the only
270
- # instruction). `description` stays as the short label whose
271
- # `profile_interview:<id>` prefix triggers the Operation 6 sub-flow.
272
- curl -s -X POST http://localhost:8321/api/schedule \
273
- -H 'Content-Type: application/json' \
274
- -d '{"time":"<ISO>","taskType":"dm_session","prompt":"profile_interview:<id> — run the scheduled.dm ## Profile interview sub-flow (Operation 6): fire-time slot-filled abort check, then compose one short natural DM around <hint>.","description":"profile_interview:<id> — <hint>","tier":"medium","taskContext":{"scheduledBy":"user_profile_sweep_fallback","queueId":"<id>","importance":"low"}}'
196
+ # Fallback DM scheduling (Operation 5B only) — see the canonical
197
+ # POST /api/schedule example in references/sweep-and-fallback.md Op 5B.
198
+ # `prompt` is REQUIRED (the wake-up session has NO memory); `description`
199
+ # carries the `profile_interview:<id>` prefix that triggers Operation 6.
275
200
  ```
276
201
 
277
202
  The PATCH `section` argument is snake_case of the heading: `pending`,
@@ -12,7 +12,7 @@ composes; it is one of the few naturally-occurring opportunities to
12
12
  slip in a question without surfacing a cold standalone DM.
13
13
 
14
14
  ```
15
- 1. GET agent/profile-questions.md ## In Progress.
15
+ 1. GET state/profile-questions.md ## In Progress.
16
16
  2. If no entry has state=latent, skip.
17
17
  2.5 Slot-filled pre-check (MANDATORY). Same recipe as Operation 2
18
18
  step 2.5: GET /api/profile-questions/slot-filled. If
@@ -0,0 +1,88 @@
1
+ ---
2
+ kind: reference
3
+ name: op-dm-handler
4
+ description: Operations 2 and 4 — the DM-handler latent-opportunity weave and the answer-capture queue reconcile. Slot-filled pre-check, naturalness rules, two PATCHes, the "one DM = one tick" rationale.
5
+ ---
6
+
7
+ # Operation 2 — Latent opportunity check (DM handler)
8
+
9
+ Run this AFTER the standard "Capture user info" block in
10
+ `message.received.dm.md` / `message.received.dm_first.md`, BEFORE
11
+ composing the reply.
12
+
13
+ ```
14
+ 1. GET state/profile-questions.md ## In Progress.
15
+ 2. If no entry has state=latent, skip — return to the normal reply path.
16
+ 2.5 Slot-filled pre-check (MANDATORY before any weaving decision).
17
+ GET /api/profile-questions/slot-filled?path=<target>&section=<section?>&anchor=<anchor?>
18
+ for the latent row's target. If `filled: true`, the slot was filled
19
+ between morning routine and now (the user may have volunteered the
20
+ answer in a previous DM, or the sweep that runs at 17:50 hasn't
21
+ caught up yet). Resolve the row instead of weaving:
22
+ - Tick the matching ## Pending row [ ] → [x] (read-rebuild + replace).
23
+ - Remove the entry from ## In Progress.
24
+ - Append `- [x] <today> → <id> (reconciled:opportunity)` to ## Answered.
25
+ - Remove the matching `Profile question (latent): <id>` line from
26
+ today.md ## Agent Notes.
27
+ Return to the normal reply path — DO NOT weave a question. The user
28
+ has effectively already answered it.
29
+ 3. Otherwise (slot still empty), judge whether the inbound DM is a
30
+ natural moment to ask the question. The criteria:
31
+ - Topic match. The user's message touches the question's domain
32
+ (work questions when the user mentions a meeting, code, deadline;
33
+ personal questions when the user discusses lifestyle, hobbies,
34
+ weekend plans; identity / location questions when the user makes
35
+ a general greeting on day 1 or asks for time/weather).
36
+ - Length appropriateness. Your reply will be more than a one-liner;
37
+ there is room to weave a question without making the message look
38
+ forced.
39
+ - Mood appropriateness. The user is not venting / in crisis / asking
40
+ a single short factual question.
41
+ 4. If yes:
42
+ a. Compose the reply with the question woven in NATURALLY. Do NOT
43
+ make a separate paragraph or "By the way, …" preamble. The
44
+ question should feel like a side comment, not an interview cue.
45
+ b. After the reply is ready (final assistant text already includes
46
+ the question), issue **two PATCH replaces** (separate calls —
47
+ they target different files):
48
+ - `PATCH /api/context/state/profile-questions` section=in_progress
49
+ — flip the entry to
50
+ `state=asked :: since=<unchanged> :: asked_at=<current_time>`
51
+ (preserve the original `since=` date).
52
+ - `PATCH /api/context/state/today` section=agent_notes — replace the
53
+ parenthetical from `(latent)` to `(asked HH:MM)` and append
54
+ `(asked HH:MM)` to the same line; preserve every other Agent
55
+ Notes line byte-for-byte.
56
+ 5. If no, leave the latent entry untouched. Return to the normal reply
57
+ path. The opportunity will come (or the evening sweep will eventually
58
+ promote to a fallback scheduled DM if it doesn't).
59
+ ```
60
+
61
+ # Operation 4 — Capture the answer (DM handler queue reconcile)
62
+
63
+ Run this AFTER "Capture user info" and Operation 2's opportunity check,
64
+ BEFORE composing the reply text.
65
+
66
+ ```
67
+ 1. GET state/profile-questions.md ## In Progress.
68
+ 2. If an entry has state=asked AND (now − asked_at) < 24h:
69
+ a. Tick the matching ## Pending row [ ] → [x] via PATCH replace
70
+ (read-rebuild full Pending body, change just that one line).
71
+ b. Remove the entry from ## In Progress (PATCH replace).
72
+ c. Append `- [x] <today> → <id> (DM)` to ## Answered.
73
+ d. Remove the matching today.md ## Agent Notes line — match by
74
+ the `Profile question (` prefix and the `: <id>` id segment;
75
+ works for both `(asked HH:MM)` and `(latent)` parenthetical
76
+ states (PATCH replace, read-rebuild — preserve every other line
77
+ byte-for-byte). Without this the line lingers in today.md
78
+ until tomorrow's PUT-replace and confuses the user.
79
+ 3. If state=latent or state=scheduled, leave the entry alone — this
80
+ inbound DM is by definition unrelated to a question the user has
81
+ not been asked.
82
+ ```
83
+
84
+ The "one DM after fire = one tick" rule is intentional. Trying to detect
85
+ "did the user ACTUALLY answer?" is unreliable and would re-ask forever
86
+ on partial answers. The 7-day `last_attempted` cooldown means an
87
+ unresolved fact will surface again later through the natural-volunteering
88
+ path or the next opportunity.
@@ -8,11 +8,11 @@ description: Operation 1 — pick a profile-interview question during Morning Ro
8
8
 
9
9
  Run this only inside `routine.morning_routine` (and its split
10
10
  variants `routine.morning_routine_today`). DM handlers, scheduled.dm
11
- briefings, and hourly checks do not run this operation — they use
11
+ briefings, and activity scans do not run this operation — they use
12
12
  Operation 2 / 3 / 4 instead.
13
13
 
14
14
  ```
15
- 1. GET agent/profile-questions.md.
15
+ 1. GET state/profile-questions.md.
16
16
  2. Skip the entire step if any of:
17
17
  - ## In Progress is non-empty.
18
18
  - ## Pending is empty.
@@ -79,7 +79,7 @@ Triggered when a `dm_session` task description starts with
79
79
  5B) where 3 days passed without a natural opportunity.
80
80
 
81
81
  ```
82
- 1. Fire-time abort. GET agent/profile-questions.md and the row's
82
+ 1. Fire-time abort. GET state/profile-questions.md and the row's
83
83
  target_path. Call /api/profile-questions/slot-filled. If the slot is
84
84
  filled per the heuristic OR the matching Pending row is no longer
85
85
  `[ ]`:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: user-profile
3
- description: Record user facts — identity, people, work, expertise, habits, goals, tendencies. Top-level → user/profile.md; detail → user/<topic>.md. Tone/style/voice/language are NOT facts — route to PATCH /api/config/character. Same turn as reply. Skip duplicates. Never notify.
3
+ description: Record user facts — identity, people, work, expertise, habits, goals, tendencies. Top-level → identity/profile.md; detail → identity/<topic>.md. Tone/style/voice/language are NOT facts — route to PATCH /api/config/character. Same turn as reply. Skip dupes. Never notify.
4
4
  allowed-tools:
5
5
  - Bash(curl *)
6
6
  - Read
@@ -8,11 +8,11 @@ allowed-tools:
8
8
 
9
9
  # User Profile Update Guide
10
10
 
11
- Output language: `identity/profile.md` and `user/*.md` are Policy B — see `<output_language_policy>`. Template H2 headers stay English skeleton; the facts under them are in `<settings primary_language>`. Preserve user-customized headers verbatim.
11
+ Output language: `identity/profile.md` and `identity/*.md` are Policy B — see `<output_language_policy>`. Template H2 headers stay English skeleton; the facts under them are in `<settings primary_language>`. Preserve user-customized headers verbatim.
12
12
 
13
13
  `identity/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).
14
14
 
15
- Detailed, dictionary-like background belongs under `user/*.md`. Read `identity/_index.md` first, then fetch only the topic file you need.
15
+ Detailed, dictionary-like background belongs under `identity/*.md`. Read `identity/_index.md` first, then fetch only the topic file you need.
16
16
 
17
17
  ## When to Update
18
18
 
@@ -37,34 +37,24 @@ Detailed, dictionary-like background belongs under `user/*.md`. Read `identity/_
37
37
  - "user has been unusually curt over the last week" — a pattern inferred across multiple turns with no single user statement. Written to `profile.md ## Learned Context` by Evening Review Step 3a only; the DM handler and sweep do not write inferred patterns.
38
38
  - "my sister just had a baby" → `identity/people.md ## Family`. If the section doesn't exist yet (fresh topic file with only the H1), the PATCH returns `section_not_found` — retry with `mode: "append_to_file"` and include `"\n## Family\n- <bullet>"` in `content`. The next write to that section succeeds normally.
39
39
 
40
- The decision rule (profile.md vs `user/<topic>.md` tie-breakers, `section_not_found` → `append_to_file` first-write fallback, and the read-before-write `curl` recipe) is documented in full in this skill — see §"Section ownership", §"File schema", and §"Worked example" below.
40
+ The decision rule (profile.md vs `identity/<topic>.md` tie-breakers, `section_not_found` → `append_to_file` first-write fallback, and the read-before-write `curl` recipe) is documented in full in this skill — see §"Section ownership", §"File schema", and §"Worked example" below.
41
41
 
42
42
  ## Section ownership
43
43
 
44
- | Section | Update trigger | Who writes |
45
- |---|---|---|
46
- | Identity | Explicit statement | DM handler, sweep |
47
- | Work Pattern | Explicit statement | DM handler, sweep |
48
- | Platforms | Explicit statement | Setup, explicit change |
49
- | Expertise | Explicit statement (summary) | DM handler, sweep |
50
- | Notification Preferences | Explicit statement | DM handler, sweep |
51
- | Learned Context | Stated preferences (DM) + Raw Signals graduation (evening) | DM handler, sweep, Evening Review 3a |
52
- | Raw Signals | Behavioral detection | SignalDetector only |
53
- | `user/<topic>.md` (people / work / expertise / personal / goals) | Any detail-heavy fact per the capture decision rule in the DM task-flow | DM handler, `routine.user_profile_sweep` |
44
+ Per-section trigger destination writer is in the auto-curated **When-to-update routing** block below. Writer notes layered on top:
54
45
 
55
- "DM handler" above covers both `message.received.dm` and `message.received.dm_first`. "Sweep" is `routine.user_profile_sweep` (fires 03:50 and 17:50 local; see its task-flow). Evening Review Step 3a is an additional writer to Learned Context only, synthesizing entries from Raw Signals graduation.
46
+ - "DM handler" covers both `message.received.dm` and `message.received.dm_first`. "Sweep" is `routine.user_profile_sweep` (fires 03:50 and 17:50 local; see its task-flow). Evening Review Step 3a is an additional writer to Learned Context only, synthesizing entries from Raw Signals graduation.
47
+ - **Do not write to Raw Signals from other events — that section is `SignalDetector`'s alone.**
56
48
 
57
- **Do not write to Raw Signals from other events — that section is `SignalDetector`'s alone.**
49
+ ## identity/profile.md vs identity/<topic>
58
50
 
59
- ## user/profile.md vs user/
51
+ **identity/profile.md** injected every session: Identity, Work Pattern, Platforms, Expertise summary, Notification Preferences, Learned Context.
60
52
 
61
- **user/profile.md** — injected every session: Identity, Work Pattern, Platforms, Expertise summary, Notification Preferences, Learned Context.
62
-
63
- **user/*.md** — dictionary-like (`people` | `work` | `expertise` | `goals` | `personal`), too detailed for every session. Read `_index.md` first → fetch only the relevant topic.
53
+ **identity/*.md** — dictionary-like (`people` | `work` | `expertise` | `goals` | `personal`), too detailed for every session. Read `identity/_index.md` first fetch only the relevant topic. Per-topic ownership is in the auto-curated **Knowledge map** block below.
64
54
 
65
55
  ## File schema
66
56
 
67
- All `user/*.md` files must keep YAML frontmatter with `type: user`,
57
+ All `identity/*.md` files must keep YAML frontmatter with `type: user`,
68
58
  `owner: shared`, and `updated: YYYY-MM-DD`, followed by an H1. When using
69
59
  section-level PATCH, preserve the existing frontmatter. When doing a
70
60
  full-file PUT, update `updated` to today's date.
@@ -106,7 +96,7 @@ If the user says "I don't want work notifications on weekends", paraphrase into
106
96
  preferences are NOT profile content.** Route them to the `character`
107
97
  runtime-config field via `PATCH /api/config/character` (narrow
108
98
  endpoint, 1000-char cap, read-before-write). Never write tone /
109
- style preferences into `identity/profile.md` or any `user/*.md`.
99
+ style preferences into `identity/profile.md` or any `identity/*.md`.
110
100
 
111
101
  Full recipe — triggers, merge rules, endpoint note, cap-handling, and
112
102
  where the value ends up — is in the character-preferences reference
@@ -120,7 +110,7 @@ See _safety.md "Common Patterns" for the general rule. Section name in PATCH is
120
110
 
121
111
  ### Worked example
122
112
 
123
- User: `"I want to read 20 books this year."` → GET user/profile.md (or topic file), merge new bullet into the right section. For a top-level goal summary bullet:
113
+ User: `"I want to read 20 books this year."` → GET identity/profile.md (or topic file), merge new bullet into the right section. For a top-level goal summary bullet:
124
114
  ```bash
125
115
  curl -s -X PATCH http://localhost:8321/api/context/identity/profile \
126
116
  -H 'Content-Type: application/json' \
@@ -130,7 +120,7 @@ For a full-section replace, GET first, merge with existing bullets, then PATCH w
130
120
 
131
121
  **WRONG** (erases existing bullets): `curl -s -X PATCH ... -d '{"section": "learned_context", "mode": "replace", "content": "- [2026-04-23] Reading goal: ..."}'` when the section already held other bullets.
132
122
 
133
- For writes to `user/<topic>.md` (people / work / expertise / personal / goals), the same decision rule applies — §"Routing edge cases" above documents the `section_not_found` → `append_to_file` first-write fallback (with a worked `curl` example against `identity/people.md`). The read-before-write rule applies identically when merging into an existing `user/<topic>.md` section.
123
+ For writes to `identity/<topic>.md` (people / work / expertise / personal / goals), the same decision rule applies — §"Routing edge cases" above documents the `section_not_found` → `append_to_file` first-write fallback (with a worked `curl` example against `identity/people.md`). The read-before-write rule applies identically when merging into an existing `identity/<topic>.md` section.
134
124
 
135
125
  ## Learned Context entry format
136
126
 
@@ -150,7 +140,7 @@ Review can prune entries older than 30 days:
150
140
  - **Silent updates.** Never notify the user about profile changes.
151
141
  - **Keep concise.** 2–5 bullets per section max. Consolidate similar bullets.
152
142
  - **No duplicates.** Scan before adding. Prefer refining an existing bullet.
153
- - **Total budget ~600 tokens** for user/profile.md. Consolidate aggressively.
143
+ - **Total budget ~600 tokens** for identity/profile.md. Consolidate aggressively.
154
144
  - **Don't write verbatim user messages.** Paraphrase into stable preference statements.
155
145
 
156
146
  ## Initial Setup
@@ -166,7 +156,7 @@ Populate `identity/profile.md` (Identity, Work Pattern, Platforms, Expertise sum
166
156
  The generic GET / PATCH surface (modes, fields, error envelopes) is
167
157
  documented in the **context** skill `references/api.md`. user-profile
168
158
  writes target the two paths `/api/context/identity/profile` (the injected
169
- summary file) and `/api/context/user/:topic` (one of `people` /
159
+ summary file) and `/api/context/identity/:topic` (one of `people` /
170
160
  `work` / `expertise` / `personal` / `goals`).
171
161
 
172
162
  Two user-profile-specific notes layered on top of the generic surface:
@@ -6,8 +6,8 @@
6
6
  "kind": "knowledge_layout",
7
7
  "anchor": "<!-- CURATION:knowledge_layout id=\"topic-files\" -->",
8
8
  "human_label": "Topic file layout",
9
- "description": "Which user/* file owns which kind of fact",
10
- "scope_paths": ["identity/profile.md", "user/*.md"]
9
+ "description": "Which identity/* file owns which kind of fact",
10
+ "scope_paths": ["identity/profile.md", "identity/*.md"]
11
11
  },
12
12
  {
13
13
  "id": "routing-table",
@@ -15,7 +15,7 @@
15
15
  "anchor": "<!-- CURATION:routing_table id=\"routing-table\" -->",
16
16
  "human_label": "When-to-update routing rules",
17
17
  "description": "Trigger phrases mapped to destination file, section, and write mode",
18
- "scope_paths": ["user/*.md"]
18
+ "scope_paths": ["identity/*.md"]
19
19
  },
20
20
  {
21
21
  "id": "learned-context-format",
@@ -10,7 +10,7 @@ description: Tone / style / voice / language preferences are agent directives
10
10
  are NOT profile content** — they are agent directives, not user facts.
11
11
  Route them to the `character` runtime-config field via `PATCH
12
12
  /api/config/character` (see `docs/design/15-character.md`), never to
13
- `identity/profile.md` or any `user/*.md`.
13
+ `identity/profile.md` or any `identity/*.md`.
14
14
 
15
15
  ## Triggers
16
16
 
@@ -78,6 +78,6 @@ uniformly — no separate injection per backend.
78
78
  ## What does NOT belong here
79
79
 
80
80
  Facts about the user — identity, role, expertise, hobbies, people,
81
- goals — go to `identity/profile.md` or `user/<topic>.md`, not to
81
+ goals — go to `identity/profile.md` or `identity/<topic>.md`, not to
82
82
  `character`. The split is: `character` says *how* the agent speaks;
83
- `user/*` says *who the user is*.
83
+ `identity/*` says *who the user is*.
@@ -9,7 +9,7 @@ allowed-tools:
9
9
 
10
10
  You run under process key `wiki.ask`.
11
11
 
12
- Read `<wiki_command>` for the user's `question`. Search and read relevant `20_wiki/` notes first; read `10_raw/` only when the wiki notes need source verification.
12
+ Read `<wiki_command>` for the user's `question`. Search and read relevant `20_wiki/` notes first (see wiki-vault-rules for `/search` and `/index`); read `10_raw/` only when the wiki notes need source verification.
13
13
 
14
14
  Write the answer to:
15
15
 
@@ -26,7 +26,7 @@ Therefore:
26
26
  | Method | Path | Purpose |
27
27
  |---|---|---|
28
28
  | `GET` | `/api/wiki/{{workspace_name}}/index` | List every file in the workspace with `path`/`mtime`/`sizeBytes`. Primary enumeration tool. |
29
- | `GET` | `/api/wiki/{{workspace_name}}/search?q=<query>` | Body-substring search for duplicate detection. |
29
+ | `GET` | `/api/wiki/{{workspace_name}}/search?q=<query>` | Duplicate detection. Defaults to FTS5 token matching; append `&kind=grep` for literal substring matches. |
30
30
  | `GET` | `/api/wiki/{{workspace_name}}/files/<path>` | Read raw notes, existing wiki notes, taxonomy, log. |
31
31
  | `POST` | `/api/wiki/{{workspace_name}}/files/20_wiki/<slug>.md` | Create a new wiki note. |
32
32
  | `POST` | `/api/wiki/{{workspace_name}}/files/20_wiki/<slug>.md` | Rewrite an existing wiki note — re-POST to the same path overwrites in place (a `.snapshots/` backup is taken first). |
@@ -93,7 +93,7 @@ Inside `<<'JSON'` (single-quoted marker) the body is verbatim — only JSON's ow
93
93
  ## Anti-patterns (silent denials — never claim success after these)
94
94
 
95
95
  - `Write` / `Edit` tools — **stripped from the session allow-list** for every `wiki.*` process key. The SDK denies them silently under `dontAsk` ("Permission to use Write has been denied …"). There is no path-rewrite that makes them work; use the Wiki API via curl. The agent that hit "Write denied → reported failure" is the canonical failure mode this section exists to prevent.
96
- - `Bash(find ...)`, `Bash(ls ...)`, `Bash(cat ...)`, `Bash(grep ...)`, `Bash(wc ...)`, and every other shell utility — silently denied. Only `Bash(curl *)` and `Bash(jq *)` are on the allow-list. Enumerate via `GET /api/wiki/<ws>/index`, not from disk.
96
+ - `Bash(find ...)`, `Bash(ls ...)`, `Bash(cat ...)`, `Bash(grep ...)`, `Bash(wc ...)`, and every other shell utility — silently denied. Only `Bash(curl *)` and `Bash(jq *)` are on the allow-list (`jq` runs standalone or as `curl … | jq`). Enumerate via `GET /api/wiki/<ws>/index`, not from disk.
97
97
  - `echo '{...}' | curl ...`, `cat <<JSON | curl ... -d @- JSON`, `bash -c "curl ..."`, `( curl ... )`, `var=... curl ...`, `curl ... ; curl ...` — Bash command does not start with `curl`; silently denied. The reverse — `curl ... -d @- <<'JSON' … JSON` on the same line — DOES start with `curl` and IS allowed (the heredoc redirects into curl's stdin).
98
98
  - `curl ... -d @/some/path` — `@<filepath>` is blocked by the security hook and the shim. The only acceptable `@…` value is the literal stdin marker `@-`.
99
99
  - `curl http://example.com/...` (non-loopback) — only `http://localhost:8321/api/*` is permitted.
@@ -104,10 +104,11 @@ Inside `<<'JSON'` (single-quoted marker) the body is verbatim — only JSON's ow
104
104
 
105
105
  | Status | Body code | Cause | Recovery |
106
106
  |---|---|---|---|
107
- | 400 | `invalid_body` | JSON body did not parse / `content` not a string | Switch to the heredoc shape if you were inline-escaping a multi-KB body. |
107
+ | 400 | `invalid_json_body` | JSON body did not parse (checked first, before Zod) | Switch to the heredoc shape if you were inline-escaping a multi-KB body. |
108
+ | 400 | `invalid_body` | Body parsed but `content` is not a string (Zod rejection) | Ensure `content` is a JSON string. |
108
109
  | 400 | `invalid_path` / `invalid_layer` | Slug shape or layer prefix rejected | Path must be exactly `20_wiki/<slug>.md`. Slug matches `^[a-z0-9][a-z0-9-]*$`. |
109
110
  | 403 | `missing_process_key` | Header missing | Add `-H 'x-process-key: wiki.compile'`. |
110
- | 403 | `wiki_write_denied` | Trying to write outside `20_wiki/` (or the `_index.md` / `log.md` exceptions) under `wiki.compile` | Fix the target path. Use `wiki-graduate` for inbox-to-wiki promotion (same process key, different source layer). |
111
+ | 403 | `wiki_write_denied` | A non-`wiki.compile` key targeting the `20_wiki` layer. `_index.md` is a normal `20_wiki` file; `log.md` is its own append-only layer (PATCH-only). | Fix the target path. Use `wiki-graduate` for inbox-to-wiki promotion (same process key, different source layer). |
111
112
  | 409 | `append_only` | POST to an existing `10_raw/` file or `log.md` (those two layers are create-only). Does NOT apply to `20_wiki/<slug>.md` — re-POSTing there overwrites in place. | For `log.md` use `PATCH mode: "append"`. `10_raw/` files cannot be overwritten by design. |
112
113
  | 413 | — | Body exceeds 512 KB | Split the note or trim verbatim extracts. |
113
114
  | 5xx | — | Daemon error | Append a failure line to `log.md`, classify the run as partial, do not retry inside the same turn. |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wiki-connect
3
- description: Load for wiki.connect. Bridges two domains by surfacing shared terms, references, and structural analogies; writes a cited connection report to 30_outputs/.
3
+ description: Bridge two wiki domains on /connect A B surface shared terms, common references, and structural analogies, then write a cited connection report to 30_outputs/. Runs under process key wiki.connect.
4
4
  allowed-tools:
5
5
  - Bash(curl *)
6
6
  ---
@@ -11,6 +11,16 @@ You run under process key `wiki.connect`.
11
11
 
12
12
  Read `<wiki_command>` for the two topics — fields `topic_a` and `topic_b`. Your job is to find genuine bridges between the two domains using only what the wiki contains. Speculative analogies are out of scope; if there is no real overlap, say so plainly.
13
13
 
14
+ ## Endpoints
15
+
16
+ Every request includes `-H 'x-process-key: wiki.connect'`. Body shapes:
17
+
18
+ | Method | Path | Body |
19
+ |---|---|---|
20
+ | `GET` | `/api/wiki/{{workspace_name}}/search?q=<topic>` | — |
21
+ | `POST` | `/api/wiki/{{workspace_name}}/files/30_outputs/<file>.md` | `{"content":"<report-md>"}` |
22
+ | `PATCH` | `/api/wiki/{{workspace_name}}/files/log.md` | `{"mode":"append","content":"<line>\n"}` |
23
+
14
24
  ## Method
15
25
 
16
26
  1. Search each topic independently:
@@ -30,11 +40,18 @@ Read `<wiki_command>` for the two topics — fields `topic_a` and `topic_b`. You
30
40
 
31
41
  Write exactly one report:
32
42
 
33
- ```
34
- POST /api/wiki/{{workspace_name}}/files/30_outputs/<YYYY-MM-DD>-connect-<slug>.md
35
- x-process-key: wiki.connect
43
+ ```bash
44
+ curl http://localhost:8321/api/wiki/{{workspace_name}}/files/30_outputs/<YYYY-MM-DD>-connect-<slug>.md \
45
+ -X POST \
46
+ -H 'content-type: application/json' \
47
+ -H 'x-process-key: wiki.connect' \
48
+ -d @- <<'JSON'
49
+ {"content":"<report-md>"}
50
+ JSON
36
51
  ```
37
52
 
53
+ The body is `{"content":"<report-md>"}` — `wikiFilePostSchema` requires the `content` field. An empty body returns `400 invalid_json_body`; valid JSON missing `content` returns `400 invalid_body`. Always send the `content` field.
54
+
38
55
  - `<YYYY-MM-DD>` is today's date.
39
56
  - `<slug>` is `<slug-a>--<slug-b>` using the canonical slugs from `90_meta/taxonomy.md` when available, kebab-cased and joined by a double hyphen.
40
57
 
@@ -59,7 +76,17 @@ Report shape:
59
76
  - proposed wiki slug, one-sentence rationale, suggested anchor notes
60
77
  ```
61
78
 
62
- If no genuine bridges exist, write the report anyway with empty sections marked `_(none)_` and a `## Summary` that names that clearly — a "no connection" finding is still useful to the owner. Append a one-line `log.md` entry referencing the output filename and both topics.
79
+ If no genuine bridges exist, write the report anyway with empty sections marked `_(none)_` and a `## Summary` that names that clearly — a "no connection" finding is still useful to the owner.
80
+
81
+ Then append a one-line `log.md` entry referencing the output filename and both topics. `log.md` is append-only — POST to an existing `log.md` returns `409 'Use PATCH to append log.md'`. Use PATCH:
82
+
83
+ ```bash
84
+ curl http://localhost:8321/api/wiki/{{workspace_name}}/files/log.md \
85
+ -X PATCH \
86
+ -H 'content-type: application/json' \
87
+ -H 'x-process-key: wiki.connect' \
88
+ -d '{"mode":"append","content":"[<ISO>] wiki.connect: <topic_a> ↔ <topic_b> → 30_outputs/<file>.md\n"}'
89
+ ```
63
90
 
64
91
  ### Completion message (mandatory)
65
92
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wiki-ingest
3
- description: Load for wiki.ingest_url. Captures one URL into 10_raw/<slug>.md by POSTing through the daemon Wiki API. Carries the canonical curl invocation, the path / response contract, and the strict success-verification rule.
3
+ description: Load for wiki.ingest_url. Use when ingesting one shared URL/article into the wiki raw layer (10_raw) via the daemon Wiki API. Carries the canonical curl invocation, the path / response contract, and the strict success-verification rule.
4
4
  allowed-tools:
5
5
  - WebFetch
6
6
  - Bash(curl *)
@@ -47,7 +47,7 @@ Do NOT call any other daemon endpoint. The daemon has **no** `/api/send-message`
47
47
  - `## Source extracts` — verbatim or close-paraphrase quotes.
48
48
  - `## Open questions` — anything you could not verify from the page.
49
49
  4. **POST the note** via the canonical curl shape (next section). Inspect the response body.
50
- 5. **Verify**: the response body must be exactly `{"ok":true,"path":"10_raw/<slug>.md"}`. If anything else (4xx/5xx, missing `ok`, wrong path, no body, `PA_API_ERROR …` on stderr), treat it as failure. Follow the error table below or PATCH `log.md` and emit the failure DM.
50
+ 5. **Verify**: the response body must be exactly `{"ok":true,"path":"10_raw/<slug>.md"}`. If anything else (4xx/5xx, missing `ok`, wrong path, no body, `PA_API_ERROR …` on stderr), treat it as failure. Follow the error->recovery table in "Curl mechanics" or PATCH `log.md` and emit the failure DM.
51
51
  6. **Emit the completion DM** (last section). Success or failure, exactly one line, no extra prose.
52
52
 
53
53
  ## Path shape — exact, no nesting
@@ -119,59 +119,15 @@ curl http://localhost:8321/api/wiki/{{workspace_name}}/files/log.md \
119
119
  -d '{"mode":"append","content":"[2026-05-13T05:08:00Z] wiki.ingest_url failed https://news.example.com/path/123 — fetch returned 403\n"}'
120
120
  ```
121
121
 
122
- ## Body-quoting rules
122
+ ## Curl mechanics, anti-patterns, and error recovery
123
123
 
124
- **Heredoc body (recommended for article bodies):** wrap with a single-quoted marker (`<<'JSON' JSON`). The body is verbatim — shell does NOT expand `$`, backticks, or quotes. Only JSON's own escapes apply: `\"`, `\\`, `\n`. The line `JSON` must appear at column 0 with no trailing characters.
124
+ Body-quoting rules, the silent-denial anti-patterns, and the per-status error->recovery table live on demand:
125
125
 
126
- **Inline body (short bodies only):** wrap the JSON in outer single quotes; inside, use JSON escapes:
127
-
128
- | Need in `content` | Write in shell-arg |
129
- |---|---|
130
- | `"` | `\"` |
131
- | `\` | `\\` |
132
- | newline | `\n` |
133
- | `'` | `'\''` (close-escape-reopen), or substitute `’` (U+2019) |
134
- | `$`, backticks | leave as-is (single quotes suppress expansion) |
135
-
136
- Keep the note compact — well under 512 KB. Curate 5-20 KB of the most informative extracts rather than dumping the whole page.
137
-
138
- ## Anti-patterns (silent denials / 4xx — never claim success after these)
139
-
140
- - `WebFetch http://localhost:8321/...` — Claude WebFetch refuses loopback. Use curl.
141
- - `echo '{...}' | curl ...`, `cat <<JSON | curl ... -d @- JSON ; …`, `bash -c "curl ..."` — Bash command does not start with `curl`; denied silently under `dontAsk`. (Heredoc redirected directly into curl on the same line — `curl ... -d @- <<'JSON' … JSON` — IS allowed because the command still starts with `curl`.)
142
- - `curl ... -d @/tmp/body.json` — `@<path>` form is blocked by the security hook and by the shim.
143
- - `curl http://example.com/...` (non-loopback) — security hook denies; only `http://localhost:8321/api/*` is permitted.
144
- - POST to a non-existent path like `/api/send-message`, `/api/whatsapp/send`, `/api/notify-user`, `/api/dm`. These are NOT daemon routes; calls return 401/404 and DO NOT notify anyone.
145
- - POST/PATCH to `/api/wiki/.../10_raw/<slug>.md` a second time — raw is create-only; the second call returns 409 and **does NOT** modify the file.
146
- - `Write` / `Edit` tools — stripped from the session allow-list for every `wiki.*` process key. The SDK denies them silently under `dontAsk` (you'll see "Permission to use Write has been denied …"). There is no path-rewrite that makes them work; use the Wiki API via curl. `Bash(find ...)`, `Bash(ls ...)`, `Bash(cat ...)` and other shell utilities are also denied — only `Bash(curl *)` and `Bash(jq *)` are on the allow-list.
147
-
148
- ## Troubleshooting
149
-
150
- Every non-2xx response causes the curl shim to write one line to stderr:
151
-
152
- ```
153
- PA_API_ERROR {"method":"POST","path":"/api/wiki/...","status":<n>,"bodyPreview":"<json error>"}
154
- ```
155
-
156
- Read `status` + `bodyPreview` and react:
157
-
158
- | Status | Body code | Cause | Recovery |
159
- |---|---|---|---|
160
- | 200 | (response is `{"ok":true,"path":...}`) | Success | Emit the success DM. |
161
- | 200 | (response missing `ok` or `path`) | Should not happen — but if it does, treat as failure | Emit failure DM. |
162
- | 400 | `invalid_body` | JSON body did not parse / `content` not a string | Re-emit the body. For inline `-d` check your single-quote / `\n` / `\"` escapes. If the body was `@-` literally, the heredoc was missed — switch to the heredoc shape (`-d @- <<'JSON' … JSON` on the same line as curl). |
163
- | 400 | `invalid_path` / `invalid_layer` | Slug or layer rejected | Path must be **exactly** `10_raw/<slug>.md`, slug matching `^[a-z0-9][a-z0-9-]*$`. No nested folders. |
164
- | 403 | `missing_process_key` | Header missing | Add `-H 'x-process-key: wiki.ingest_url'` and retry. |
165
- | 403 | `raw_write_denied` | Process key isn't `wiki.ingest_url` | Configuration error; emit failure DM, do not retry. |
166
- | 409 | `append_only` | Slug already exists in `10_raw/` | Suffix the slug (`<slug>-2`), retry the POST **once**. If `-2` also 409, PATCH log.md and emit failure DM — do not loop further. |
167
- | 413 | (body too large) | Article > 512 KB | Trim verbatim extracts; keep essentials. |
168
- | 5xx | — | Daemon error | PATCH log.md and emit failure DM. Do not retry — the daemon will not heal mid-turn. |
169
-
170
- If the Bash call returns to the prompt with **no stdout body and no `PA_API_ERROR`**, your command did not start with literal `curl` and was silently denied. Rewrite it as a flat, single-line curl invocation following the canonical shape above.
126
+ {{> ref:curl-errors }}
171
127
 
172
128
  ## Completion message (mandatory final assistant text)
173
129
 
174
- Emit exactly one line as your final assistant message. The daemon forwards this verbatim to the channel the bang came from — no other delivery mechanism is needed and you must NOT try to "send" it through another endpoint.
130
+ Emit exactly one line as your final assistant message. The daemon forwards this verbatim to the channel the bang came from — no other delivery mechanism is needed and you must NOT try to "send" it through another endpoint (no /api/send-message etc.; see "Allowed endpoints").
175
131
 
176
132
  - Success (only after seeing `{"ok":true,"path":"10_raw/<slug>.md"}`): `Ingested <url> → 10_raw/<slug>.md`
177
133
  - Failure (any other outcome): `Failed <url> — <one-sentence reason>`