@aitne-sh/aitne 0.1.8 → 0.1.10

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 (308) hide show
  1. package/README.md +251 -164
  2. package/agent-assets/agent-profiles/_safety.md +3 -3
  3. package/agent-assets/agent-profiles/browser-task.md +108 -0
  4. package/agent-assets/agent-profiles/conversational.md +3 -3
  5. package/agent-assets/agent-profiles/profile-importer.md +2 -2
  6. package/agent-assets/agent-profiles/routine-fetch-window.md +30 -19
  7. package/agent-assets/agents/context-index-reconcile/agent.md +52 -0
  8. package/agent-assets/agents/evening-review/agent.md +53 -0
  9. package/agent-assets/agents/hourly-check/agent.md +62 -0
  10. package/agent-assets/agents/monthly-review/agent.md +55 -0
  11. package/agent-assets/agents/morning-routine/agent.md +78 -0
  12. package/agent-assets/agents/roadmap-maintenance/agent.md +52 -0
  13. package/agent-assets/agents/skill-curation/agent.md +52 -0
  14. package/agent-assets/agents/user-profile-sweep-evening/agent.md +48 -0
  15. package/agent-assets/agents/user-profile-sweep-morning/agent.md +53 -0
  16. package/agent-assets/agents/weekly-review/agent.md +51 -0
  17. package/agent-assets/docs/concepts/agent-day.md +13 -11
  18. package/agent-assets/docs/concepts/auth-health.md +47 -10
  19. package/agent-assets/docs/concepts/backends-and-tiers.md +66 -31
  20. package/agent-assets/docs/concepts/costs-and-quotas.md +51 -15
  21. package/agent-assets/docs/concepts/delegated-mode.md +56 -17
  22. package/agent-assets/docs/concepts/memory-model.md +77 -34
  23. package/agent-assets/docs/concepts/observations.md +49 -11
  24. package/agent-assets/docs/concepts/process-keys.md +56 -22
  25. package/agent-assets/docs/concepts/routines.md +60 -33
  26. package/agent-assets/docs/concepts/safety-and-execution.md +50 -21
  27. package/agent-assets/docs/concepts/safety-model.md +61 -50
  28. package/agent-assets/docs/concepts/skills.md +34 -18
  29. package/agent-assets/docs/features/integrations/browser-history.md +196 -0
  30. package/agent-assets/docs/features/integrations/calendar.md +39 -29
  31. package/agent-assets/docs/features/integrations/git.md +18 -7
  32. package/agent-assets/docs/features/integrations/github.md +84 -33
  33. package/agent-assets/docs/features/integrations/mail.md +61 -17
  34. package/agent-assets/docs/features/integrations/notion.md +18 -6
  35. package/agent-assets/docs/features/integrations/obsidian.md +28 -5
  36. package/agent-assets/docs/features/lifestyle/git.md +44 -40
  37. package/agent-assets/docs/features/lifestyle/reading.md +57 -22
  38. package/agent-assets/docs/features/lifestyle/receipts.md +51 -21
  39. package/agent-assets/docs/features/lifestyle/travel-bookings.md +77 -14
  40. package/agent-assets/docs/features/memory-files/agent-journal.md +132 -53
  41. package/agent-assets/docs/features/memory-files/agent-lessons.md +177 -0
  42. package/agent-assets/docs/features/memory-files/projects.md +73 -17
  43. package/agent-assets/docs/features/memory-files/roadmap.md +54 -11
  44. package/agent-assets/docs/features/memory-files/schedule.md +113 -70
  45. package/agent-assets/docs/features/memory-files/today.md +46 -21
  46. package/agent-assets/docs/features/memory-files/user-profile.md +63 -33
  47. package/agent-assets/docs/features/messaging/bang-commands.md +113 -36
  48. package/agent-assets/docs/features/messaging/dashboard-chat.md +43 -21
  49. package/agent-assets/docs/features/messaging/discord.md +35 -4
  50. package/agent-assets/docs/features/messaging/overview.md +37 -19
  51. package/agent-assets/docs/features/messaging/pairing-and-magic-phrase.md +94 -27
  52. package/agent-assets/docs/features/messaging/slack.md +67 -14
  53. package/agent-assets/docs/features/messaging/telegram.md +22 -8
  54. package/agent-assets/docs/features/messaging/whatsapp.md +71 -17
  55. package/agent-assets/docs/features/operations/activity-and-conversations.md +45 -15
  56. package/agent-assets/docs/features/operations/approvals.md +49 -16
  57. package/agent-assets/docs/features/operations/backend-routing.md +68 -16
  58. package/agent-assets/docs/features/operations/cost-tracking.md +84 -17
  59. package/agent-assets/docs/features/operations/managed-chromium.md +222 -0
  60. package/agent-assets/docs/features/operations/notifications.md +52 -11
  61. package/agent-assets/docs/features/operations/quiet-hours.md +64 -40
  62. package/agent-assets/docs/features/operations/schedule-approaching.md +54 -24
  63. package/agent-assets/docs/features/routines/custom-routines.md +98 -26
  64. package/agent-assets/docs/features/routines/evening-review.md +82 -21
  65. package/agent-assets/docs/features/routines/hourly-check.md +149 -29
  66. package/agent-assets/docs/features/routines/morning-routine.md +54 -35
  67. package/agent-assets/docs/features/routines/weekly-review.md +46 -21
  68. package/agent-assets/docs/features/wiki/commands.md +26 -16
  69. package/agent-assets/docs/features/wiki/cost-and-approval.md +241 -0
  70. package/agent-assets/docs/features/wiki/dashboard.md +256 -0
  71. package/agent-assets/docs/features/wiki/overview.md +70 -12
  72. package/agent-assets/docs/features/wiki/search.md +248 -0
  73. package/agent-assets/docs/features/wiki/workspaces.md +254 -0
  74. package/agent-assets/docs/getting-started/01-what-is-this.md +34 -23
  75. package/agent-assets/docs/getting-started/02-first-steps.md +17 -10
  76. package/agent-assets/docs/getting-started/03-what-can-this-do.md +25 -14
  77. package/agent-assets/docs/getting-started/04-first-day.md +39 -21
  78. package/agent-assets/docs/glossary.md +235 -24
  79. package/agent-assets/docs/guides/add-a-custom-routine.md +63 -23
  80. package/agent-assets/docs/guides/backup-and-restore.md +80 -16
  81. package/agent-assets/docs/guides/budget-and-cost-for-wiki.md +57 -26
  82. package/agent-assets/docs/guides/build-your-wiki.md +22 -9
  83. package/agent-assets/docs/guides/change-which-model-handles-x.md +64 -10
  84. package/agent-assets/docs/guides/connect-a-new-mail-account.md +66 -15
  85. package/agent-assets/docs/guides/explore-with-trace-and-connect.md +32 -14
  86. package/agent-assets/docs/guides/import-knowledge-file.md +50 -40
  87. package/agent-assets/docs/guides/install-and-run.md +49 -20
  88. package/agent-assets/docs/guides/maintain-wiki-health.md +35 -10
  89. package/agent-assets/docs/guides/migrate-machines.md +74 -18
  90. package/agent-assets/docs/guides/multiple-wikis-for-multiple-domains.md +111 -60
  91. package/agent-assets/docs/guides/pause-the-agent.md +69 -24
  92. package/agent-assets/docs/guides/reinstall-cleanly.md +88 -18
  93. package/agent-assets/docs/guides/setup-wizard.md +116 -54
  94. package/agent-assets/docs/guides/switch-default-backend.md +62 -16
  95. package/agent-assets/docs/guides/use-an-existing-obsidian-vault.md +30 -14
  96. package/agent-assets/docs/reference/api.md +153 -32
  97. package/agent-assets/docs/reference/cli-commands.md +39 -18
  98. package/agent-assets/docs/reference/config.md +241 -49
  99. package/agent-assets/docs/reference/disallowed-tools.md +34 -13
  100. package/agent-assets/docs/reference/keyboard-shortcuts.md +34 -10
  101. package/agent-assets/docs/reference/knowledge-layout.md +629 -0
  102. package/agent-assets/docs/reference/process-keys.md +62 -6
  103. package/agent-assets/docs/reference/skills.md +41 -14
  104. package/agent-assets/docs/troubleshooting/auth-failed.md +51 -21
  105. package/agent-assets/docs/troubleshooting/dashboard-shows-degraded.md +97 -28
  106. package/agent-assets/docs/troubleshooting/fallback-keeps-firing.md +86 -22
  107. package/agent-assets/docs/troubleshooting/messaging-not-pairing.md +68 -24
  108. package/agent-assets/docs/troubleshooting/morning-routine-didnt-run.md +80 -20
  109. package/agent-assets/docs/troubleshooting/observation-not-detected.md +73 -21
  110. package/agent-assets/docs/troubleshooting/quota-exhausted.md +33 -8
  111. package/agent-assets/docs/troubleshooting/wiki-ingest-full-blocked.md +126 -54
  112. package/agent-assets/docs/troubleshooting/wiki-write-failed.md +29 -12
  113. package/agent-assets/optimizer-skills/drift-analysis/SKILL.md +1 -1
  114. package/agent-assets/optimizer-skills/knowledge-map/SKILL.md +1 -1
  115. package/agent-assets/optimizer-skills/skill-curation/SKILL.md +1 -1
  116. package/agent-assets/sandbox/linux/aitne-chromium.apparmor +91 -0
  117. package/agent-assets/sandbox/macos/aitne-chromium.sb +156 -0
  118. package/agent-assets/skills/agent-actions/SKILL.md +25 -41
  119. package/agent-assets/skills/agent-create/SKILL.md +158 -0
  120. package/agent-assets/skills/attach/SKILL.md +10 -29
  121. package/agent-assets/skills/browser-history/SKILL.md +211 -0
  122. package/agent-assets/skills/browser-history-respond/SKILL.md +111 -0
  123. package/agent-assets/skills/browser-task/SKILL.md +164 -0
  124. package/agent-assets/skills/context/SKILL.md +35 -44
  125. package/agent-assets/skills/context/curation.json +14 -14
  126. package/agent-assets/skills/context/references/api.md +52 -40
  127. package/agent-assets/skills/context/references/required-frontmatter.md +13 -12
  128. package/agent-assets/skills/context/references/snapshot-files.md +18 -17
  129. package/agent-assets/skills/context/seeds/file-responsibilities.seed.json +8 -8
  130. package/agent-assets/skills/context/seeds/frontmatter-requirements.seed.json +3 -3
  131. package/agent-assets/skills/docs-search/SKILL.md +23 -34
  132. package/agent-assets/skills/external-services/SKILL.delegated.claude.md +17 -114
  133. package/agent-assets/skills/external-services/SKILL.delegated.codex.md +17 -113
  134. package/agent-assets/skills/external-services/SKILL.delegated.gemini.md +17 -113
  135. package/agent-assets/skills/external-services/SKILL.md +3 -3
  136. package/agent-assets/skills/external-services/SKILL.native.claude.md +7 -7
  137. package/agent-assets/skills/external-services/SKILL.native.codex.md +7 -7
  138. package/agent-assets/skills/external-services/SKILL.native.gemini.md +4 -4
  139. package/agent-assets/skills/external-services/references/calendar-apple.md +2 -2
  140. package/agent-assets/skills/external-services/references/calendar-outlook.md +1 -1
  141. package/agent-assets/skills/external-services/references/exec-errors.md +32 -0
  142. package/agent-assets/skills/external-services/references/obsidian.md +2 -2
  143. package/agent-assets/skills/external-services/references/skills-crud.md +5 -5
  144. package/agent-assets/skills/gmail-lifestyle/SKILL.md +11 -83
  145. package/agent-assets/skills/gmail-lifestyle/references/receipts-api.md +4 -0
  146. package/agent-assets/skills/gmail-lifestyle/references/travel-bookings-api.md +9 -0
  147. package/agent-assets/skills/mail/SKILL.delegated.claude.md +15 -18
  148. package/agent-assets/skills/mail/SKILL.delegated.codex.md +11 -6
  149. package/agent-assets/skills/mail/SKILL.delegated.gemini.md +11 -6
  150. package/agent-assets/skills/mail/SKILL.md +10 -18
  151. package/agent-assets/skills/mail/SKILL.native.claude.md +8 -7
  152. package/agent-assets/skills/mail/SKILL.native.codex.md +1 -1
  153. package/agent-assets/skills/mail/SKILL.native.gemini.md +1 -1
  154. package/agent-assets/skills/mail/references/api.md +10 -3
  155. package/agent-assets/skills/mail/references/examples.md +2 -1
  156. package/agent-assets/skills/mail/references/providers.md +1 -1
  157. package/agent-assets/skills/managed-tasks/SKILL.md +48 -81
  158. package/agent-assets/skills/managed-tasks/references/errors.md +33 -19
  159. package/agent-assets/skills/managed-tasks/references/output-path.md +33 -17
  160. package/agent-assets/skills/managed-tasks/references/recurrence-rule.md +7 -5
  161. package/agent-assets/skills/management-policy/SKILL.md +42 -42
  162. package/agent-assets/skills/management-policy/curation.json +1 -1
  163. package/agent-assets/skills/management-policy/references/policy-workflow.md +11 -12
  164. package/agent-assets/skills/management-policy/seeds/policy-file-shape.seed.json +1 -1
  165. package/agent-assets/skills/notify/SKILL.md +14 -16
  166. package/agent-assets/skills/notify/references/priority.md +28 -20
  167. package/agent-assets/skills/notion/SKILL.delegated.claude.md +2 -2
  168. package/agent-assets/skills/notion/SKILL.delegated.codex.md +2 -2
  169. package/agent-assets/skills/notion/SKILL.delegated.gemini.md +2 -2
  170. package/agent-assets/skills/notion/SKILL.md +17 -17
  171. package/agent-assets/skills/notion/SKILL.native.claude.md +11 -7
  172. package/agent-assets/skills/notion/SKILL.native.codex.md +10 -5
  173. package/agent-assets/skills/notion/SKILL.native.gemini.md +10 -5
  174. package/agent-assets/skills/observations/SKILL.md +29 -28
  175. package/agent-assets/skills/observations/references/fetch-fallback.md +22 -0
  176. package/agent-assets/skills/project-doc/SKILL.md +10 -7
  177. package/agent-assets/skills/project-doc/curation.json +3 -3
  178. package/agent-assets/skills/project-doc/seeds/project-shape.seed.json +8 -5
  179. package/agent-assets/skills/project-doc/seeds/slug-grammar.seed.json +5 -5
  180. package/agent-assets/skills/reading/SKILL.md +18 -42
  181. package/agent-assets/skills/reading/references/reading-taste.md +7 -7
  182. package/agent-assets/skills/roadmap/SKILL.md +8 -24
  183. package/agent-assets/skills/roadmap/curation.json +1 -1
  184. package/agent-assets/skills/roadmap/references/api.md +21 -11
  185. package/agent-assets/skills/roadmap/references/cross-check.md +15 -8
  186. package/agent-assets/skills/roadmap/references/horizon-tags.md +11 -0
  187. package/agent-assets/skills/roadmap/references/migration.md +12 -10
  188. package/agent-assets/skills/roadmap/references/retention.md +18 -0
  189. package/agent-assets/skills/roadmap/seeds/entry-types.seed.json +1 -1
  190. package/agent-assets/skills/schedule/SKILL.md +41 -50
  191. package/agent-assets/skills/schedule/references/batch.md +2 -2
  192. package/agent-assets/skills/schedule/references/errors.md +7 -4
  193. package/agent-assets/skills/schedule/references/importance.md +23 -0
  194. package/agent-assets/skills/schedule/references/model-selection.md +3 -3
  195. package/agent-assets/skills/schedule/references/recurrence-rule.md +7 -5
  196. package/agent-assets/skills/scheduled-managed-task/SKILL.md +77 -70
  197. package/agent-assets/skills/today/SKILL.md +24 -83
  198. package/agent-assets/skills/today/curation.json +3 -3
  199. package/agent-assets/skills/today/references/agent-plan-lifecycle.md +6 -5
  200. package/agent-assets/skills/today/references/today-skeleton.md +66 -0
  201. package/agent-assets/skills/today/seeds/agent-notes-flavors.seed.json +1 -1
  202. package/agent-assets/skills/today/seeds/section-shape.seed.json +7 -7
  203. package/agent-assets/skills/user-interview/SKILL.md +21 -93
  204. package/agent-assets/skills/user-interview/references/op-briefing.md +3 -3
  205. package/agent-assets/skills/user-interview/references/op-dm-handler.md +88 -0
  206. package/agent-assets/skills/user-interview/references/op-morning.md +1 -1
  207. package/agent-assets/skills/user-interview/references/sweep-and-fallback.md +9 -1
  208. package/agent-assets/skills/user-profile/SKILL.md +29 -39
  209. package/agent-assets/skills/user-profile/curation.json +4 -4
  210. package/agent-assets/skills/user-profile/references/character-preferences.md +3 -3
  211. package/agent-assets/skills/user-profile/seeds/routing-table.seed.json +8 -8
  212. package/agent-assets/skills/user-profile/seeds/topic-files.seed.json +6 -6
  213. package/agent-assets/skills/wiki/wiki-ask/SKILL.md +1 -1
  214. package/agent-assets/skills/wiki/wiki-compile/SKILL.md +9 -8
  215. package/agent-assets/skills/wiki/wiki-connect/SKILL.md +32 -5
  216. package/agent-assets/skills/wiki/wiki-ingest/SKILL.md +6 -50
  217. package/agent-assets/skills/wiki/wiki-ingest/references/curl-errors.md +58 -0
  218. package/agent-assets/skills/wiki/wiki-lint/SKILL.md +20 -14
  219. package/agent-assets/skills/wiki/wiki-trace/SKILL.md +10 -5
  220. package/agent-assets/skills/wiki/wiki-vault-rules/SKILL.md +2 -0
  221. package/agent-assets/system-prompts/routine-fetch-window.md +22 -12
  222. package/agent-assets/task-flows/_partials/calendar-acquire.google_calendar.md +4 -2
  223. package/agent-assets/task-flows/_partials/calendar-acquire.outlook_calendar.md +4 -2
  224. package/agent-assets/task-flows/_partials/capture-user-info.md +2 -2
  225. package/agent-assets/task-flows/_partials/dm-intent.long-horizon.md +1 -1
  226. package/agent-assets/task-flows/_partials/dm-intent.project.md +9 -9
  227. package/agent-assets/task-flows/_partials/feedback-capture.md +30 -0
  228. package/agent-assets/task-flows/_partials/mail-acquire.outlook_mail.md +3 -2
  229. package/agent-assets/task-flows/_partials/notion-acquire.notion.md +10 -5
  230. package/agent-assets/task-flows/browser_task.md +84 -0
  231. package/agent-assets/task-flows/github.assigned.md +1 -1
  232. package/agent-assets/task-flows/github.pull_request.review_requested.md +2 -2
  233. package/agent-assets/task-flows/github.workflow_run.failed.md +2 -2
  234. package/agent-assets/task-flows/knowledge.import.md +14 -14
  235. package/agent-assets/task-flows/message.received.dm.md +13 -4
  236. package/agent-assets/task-flows/message.received.dm_first.md +7 -3
  237. package/agent-assets/task-flows/routine.custom.md +3 -3
  238. package/agent-assets/task-flows/routine.evening_review.md +88 -8
  239. package/agent-assets/task-flows/routine.fetch_window.md +2 -2
  240. package/agent-assets/task-flows/routine.hourly_check.md +16 -12
  241. package/agent-assets/task-flows/routine.monthly_review.md +93 -21
  242. package/agent-assets/task-flows/routine.morning_routine_journal.md +119 -97
  243. package/agent-assets/task-flows/routine.morning_routine_today.md +43 -43
  244. package/agent-assets/task-flows/routine.research_cluster_update.md +35 -0
  245. package/agent-assets/task-flows/routine.research_dispatch.md +38 -0
  246. package/agent-assets/task-flows/routine.research_offer_dm.md +125 -0
  247. package/agent-assets/task-flows/routine.research_wiki_summary.md +53 -0
  248. package/agent-assets/task-flows/routine.roadmap_refresh.md +10 -10
  249. package/agent-assets/task-flows/routine.today_refresh.md +4 -4
  250. package/agent-assets/task-flows/routine.user_profile_sweep.md +10 -10
  251. package/agent-assets/task-flows/routine.weekly_review.md +114 -24
  252. package/agent-assets/task-flows/schedule.approaching.md +0 -1
  253. package/agent-assets/task-flows/scheduled.dm.md +5 -5
  254. package/agent-assets/task-flows/scheduled.task.md +4 -4
  255. package/agent-assets/task-flows/setup.initial.md +21 -21
  256. package/agent-assets/task-flows/setup.update.md +2 -2
  257. package/agent-assets/task-flows/wiki.trace.md +1 -1
  258. package/agent-assets/templates/README.md +27 -20
  259. package/agent-assets/templates/_index.md +42 -26
  260. package/agent-assets/templates/_manifest.json +34 -99
  261. package/agent-assets/templates/{user → identity}/_index.md +1 -1
  262. package/agent-assets/templates/{user → identity}/profile.md +2 -2
  263. package/agent-assets/templates/{dossiers → knowledge/dossiers}/_index.md +1 -1
  264. package/agent-assets/templates/{projects → plans/projects}/_active.base +1 -1
  265. package/agent-assets/templates/policies/_index.md +21 -0
  266. package/agent-assets/templates/{rules → policies}/journal-export.md +1 -1
  267. package/agent-assets/templates/{rules → policies}/journal-format.md +5 -5
  268. package/agent-assets/templates/{rules/policies → policies/management-captures}/_index.md +2 -2
  269. package/agent-assets/templates/{rules → policies}/management.md +3 -3
  270. package/agent-assets/templates/{rules → policies}/mcp.md +1 -1
  271. package/agent-assets/templates/{rules → policies}/redaction.md +1 -1
  272. package/agent-assets/templates/{routines → policies/routines}/_index.md +1 -1
  273. package/agent-assets/templates/{routines → policies/routines}/evening.md +2 -2
  274. package/agent-assets/templates/{routines → policies/routines}/hourly.md +1 -1
  275. package/agent-assets/templates/{routines → policies/routines}/monthly.md +2 -2
  276. package/bin/aitne.mjs +58 -15
  277. package/package.json +5 -4
  278. package/scripts/commands/doctor.mjs +25 -10
  279. package/scripts/commands/run-now.mjs +6 -21
  280. package/scripts/lib/ports.d.mts +27 -0
  281. package/scripts/lib/ports.mjs +36 -0
  282. package/scripts/lib/process-identity.d.mts +46 -0
  283. package/scripts/lib/process-identity.mjs +193 -0
  284. package/scripts/lib/read-api-token.mjs +176 -0
  285. package/scripts/start.mjs +16 -5
  286. package/agent-assets/docs/features/lifestyle/travel-time.md +0 -58
  287. package/agent-assets/skills/gmail-lifestyle/references/travel-time-api.md +0 -59
  288. package/agent-assets/skills/schedule/references/recurring.md +0 -185
  289. package/agent-assets/templates/context-index.md +0 -42
  290. package/agent-assets/templates/rules/_index.md +0 -19
  291. /package/agent-assets/templates/{user → identity}/expertise.md +0 -0
  292. /package/agent-assets/templates/{user → identity}/goals.md +0 -0
  293. /package/agent-assets/templates/{user → identity}/people.md +0 -0
  294. /package/agent-assets/templates/{user → identity}/personal.md +0 -0
  295. /package/agent-assets/templates/{user → identity}/work.md +0 -0
  296. /package/agent-assets/templates/{agent/journal.md → journal/agent.md} +0 -0
  297. /package/agent-assets/templates/{dossiers → knowledge/dossiers}/evening.md +0 -0
  298. /package/agent-assets/templates/{dossiers → knowledge/dossiers}/hourly.md +0 -0
  299. /package/agent-assets/templates/{dossiers → knowledge/dossiers}/monthly.md +0 -0
  300. /package/agent-assets/templates/{dossiers → knowledge/dossiers}/morning.md +0 -0
  301. /package/agent-assets/templates/{dossiers → knowledge/dossiers}/roadmap.md +0 -0
  302. /package/agent-assets/templates/{dossiers → knowledge/dossiers}/weekly.md +0 -0
  303. /package/agent-assets/templates/{projects → plans/projects}/_index.md +0 -0
  304. /package/agent-assets/templates/{roadmap.md → plans/roadmap.md} +0 -0
  305. /package/agent-assets/templates/{routines → policies/routines}/morning.md +0 -0
  306. /package/agent-assets/templates/{routines → policies/routines}/weekly.md +0 -0
  307. /package/agent-assets/templates/{agent → state}/profile-questions.md +0 -0
  308. /package/agent-assets/templates/{today.md → state/today.md} +0 -0
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Process start-identity helpers for the Aitne launcher (`bin/aitne.mjs`).
3
+ *
4
+ * WHY THIS EXISTS — process-lifecycle-2 (CROSS_PLATFORM_REAUDIT_2026-06.md).
5
+ * The launcher trusts a bare PID: `process.kill(pid, 0)` is true for *any* live
6
+ * process owning that PID. After an unclean shutdown the stale `daemon.pid` /
7
+ * `dashboard.pid` can point at a PID the OS has recycled to an unrelated
8
+ * process, so `aitne start` false-positives "Already running" and `aitne stop`
9
+ * can `taskkill /T /F` (Windows) or group-kill (POSIX) the wrong process tree.
10
+ *
11
+ * FIX — pair the PID with the OS-reported process **start time**, captured at
12
+ * write time and re-read at check time. A recycled PID has a different start
13
+ * time, so an exact-string mismatch flags it stale. We compare the *same* OS
14
+ * field to *itself* on the *same* machine, so no absolute-time conversion,
15
+ * clock-skew tolerance, or locale handling is needed.
16
+ *
17
+ * WHERE THIS LIVES — `scripts/lib/` (plain ESM), because `bin/aitne.mjs` runs
18
+ * *before* the TypeScript build that produces `@aitne/shared` (running `aitne
19
+ * start` is what triggers that build) and the published package ships only
20
+ * `bin` + `scripts` + `agent-assets`. The pure functions here are pinned by a
21
+ * peer `.test.ts` under the daemon/shared `src` tree (the `ports.mjs`
22
+ * precedent), so they run under the standard vitest suite. The win32 read branch
23
+ * carries the usual "no Windows runtime validation" caveat; by construction it
24
+ * can only *degrade* to the legacy bare-PID behavior, never make things worse.
25
+ */
26
+ import { execFileSync } from "node:child_process";
27
+ import { accessSync, constants, readFileSync } from "node:fs";
28
+ import { delimiter, join } from "node:path";
29
+ import process from "node:process";
30
+
31
+ /**
32
+ * Serialize PID metadata to the pidfile.
33
+ *
34
+ * Line 1 is the bare PID so an *older* aitne (which parses only line 1) still
35
+ * reads files written by this version — downgrade-safe. The identity token is
36
+ * written as a trailing `key=value` line (not positional) so a future field can
37
+ * be added without breaking parse order; {@link parsePidMeta} already ignores
38
+ * unknown keys. An absent token is omitted.
39
+ */
40
+ export function serializePidMeta({ pid, startToken = null }) {
41
+ let out = `${pid}\n`;
42
+ if (startToken != null) {
43
+ const token = String(startToken).replace(/[\r\n]+/g, " ").trim();
44
+ if (token.length > 0) out += `start=${token}\n`;
45
+ }
46
+ return out;
47
+ }
48
+
49
+ /**
50
+ * Parse a pidfile written by {@link serializePidMeta} OR a legacy single-line
51
+ * (`<pid>\n`) file. Returns `null` if line 1 is not a finite integer. Unknown
52
+ * trailer keys are ignored, so the format stays forward-compatible.
53
+ */
54
+ export function parsePidMeta(content) {
55
+ if (typeof content !== "string") return null;
56
+ const lines = content.split(/\r?\n/);
57
+ const pid = Number.parseInt((lines[0] ?? "").trim(), 10);
58
+ if (!Number.isFinite(pid)) return null;
59
+ let startToken = null;
60
+ for (const line of lines.slice(1)) {
61
+ const eq = line.indexOf("=");
62
+ if (eq < 0) continue;
63
+ const key = line.slice(0, eq).trim();
64
+ const val = line.slice(eq + 1).trim();
65
+ if (key === "start") startToken = val.length > 0 ? val : null;
66
+ }
67
+ return { pid, startToken };
68
+ }
69
+
70
+ /**
71
+ * Extract field 22 (`starttime`, clock-ticks since boot) from `/proc/<pid>/stat`.
72
+ *
73
+ * The `comm` field (field 2) is wrapped in parens and may itself contain spaces
74
+ * and `)`, so the only safe split point is the LAST `)` — everything after it
75
+ * is space-separated starting at field 3 (state). field 22 is therefore index
76
+ * `22 - 3 = 19` in that tail. Boot-relative, so immune to wall-clock changes;
77
+ * across a reboot the recorded value's epoch is gone, which correctly reads as
78
+ * a mismatch (stale).
79
+ */
80
+ export function parseLinuxStat(statContent) {
81
+ if (typeof statContent !== "string") return null;
82
+ const close = statContent.lastIndexOf(")");
83
+ if (close < 0) return null;
84
+ const fields = statContent.slice(close + 1).trim().split(/\s+/);
85
+ const starttime = fields[19];
86
+ return starttime && /^\d+$/.test(starttime) ? starttime : null;
87
+ }
88
+
89
+ /**
90
+ * Resolve the PowerShell executable on Windows. Prefer Windows PowerShell 5.1
91
+ * (`powershell.exe`), fall back to PowerShell 7+ (`pwsh.exe`) for minimal /
92
+ * Server-Core / pwsh-7-only hosts, else keep the default so a missing host
93
+ * surfaces a clear ENOENT. Mirrors `browser-history/lifecycle/platform.ts`.
94
+ */
95
+ /* c8 ignore start -- win32-only path resolution; the POSIX test runner never enters this */
96
+ function resolveWindowsPowerShell() {
97
+ const pathValue = process.env.PATH ?? "";
98
+ const exts = process.env.PATHEXT?.split(";").filter(Boolean) ?? [".EXE"];
99
+ const probe = (name) => {
100
+ const hasExt = /\.[A-Za-z0-9]+$/.test(name);
101
+ for (const dir of pathValue.split(delimiter)) {
102
+ if (!dir) continue;
103
+ const candidates = hasExt ? [name] : exts.map((e) => `${name}${e}`);
104
+ for (const c of candidates) {
105
+ try {
106
+ accessSync(join(dir, c), constants.X_OK);
107
+ return true;
108
+ } catch {
109
+ // keep scanning
110
+ }
111
+ }
112
+ }
113
+ return false;
114
+ };
115
+ return probe("powershell.exe") ? "powershell.exe" : probe("pwsh.exe") ? "pwsh.exe" : "powershell.exe";
116
+ }
117
+ /* c8 ignore stop */
118
+
119
+ /**
120
+ * Read an opaque, OS-native start-time token for `pid`, or `null` if the PID is
121
+ * gone / the read fails. The token is compared *only* for exact-string equality
122
+ * against a token captured earlier for the same PID on the same machine, so its
123
+ * format is irrelevant as long as it is stable for a given process incarnation.
124
+ *
125
+ * `deps` is for testing — inject `platform` / `execFileSync` / `readFileSync`
126
+ * to exercise a branch off its native OS without touching the real system.
127
+ * Reads use `execFileSync` (never a shell) with a timeout; the PID is numeric,
128
+ * so there is no injection surface, but args stay arrayed on principle.
129
+ */
130
+ export function readProcessStartToken(pid, deps = {}) {
131
+ const platform = deps.platform ?? process.platform;
132
+ const exec = deps.execFileSync ?? execFileSync;
133
+ const readFile = deps.readFileSync ?? readFileSync;
134
+ if (pid == null || !Number.isFinite(Number(pid))) return null;
135
+ try {
136
+ if (platform === "linux") {
137
+ return parseLinuxStat(readFile(`/proc/${pid}/stat`, "utf8"));
138
+ }
139
+ if (platform === "darwin") {
140
+ const out = exec("ps", ["-o", "lstart=", "-p", String(pid)], {
141
+ stdio: ["ignore", "pipe", "ignore"],
142
+ timeout: 5_000,
143
+ windowsHide: true,
144
+ });
145
+ const token = String(out).replace(/[\r\n]+/g, " ").trim();
146
+ return token.length > 0 ? token : null;
147
+ }
148
+ /* c8 ignore start -- win32-only; not reachable from the POSIX test runner */
149
+ if (platform === "win32") {
150
+ const ps = resolveWindowsPowerShell();
151
+ const out = exec(
152
+ ps,
153
+ [
154
+ "-NoProfile",
155
+ "-NonInteractive",
156
+ "-Command",
157
+ `(Get-Process -Id ${Number(pid)} -ErrorAction Stop).StartTime.ToString('o')`,
158
+ ],
159
+ { stdio: ["ignore", "pipe", "ignore"], timeout: 5_000, windowsHide: true },
160
+ );
161
+ const token = String(out).replace(/[\r\n]+/g, " ").trim();
162
+ return token.length > 0 ? token : null;
163
+ }
164
+ /* c8 ignore stop */
165
+ return null;
166
+ } catch {
167
+ return null;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Classify a pidfile's recorded process against the live system.
173
+ *
174
+ * - `stale` — dead, or alive but the start-time token *differs*
175
+ * (PID recycled). Caller should remove the pidfile.
176
+ * - `running-ours` — alive and the start-time token matches.
177
+ * - `running-unverified` — alive but identity can't be confirmed: a legacy file
178
+ * with no token, or the OS start-time read failed.
179
+ * Caller treats this as running (the pre-fix bare-PID
180
+ * behavior), so the change can never regress; a legacy
181
+ * file self-heals on the next `writePid`.
182
+ *
183
+ * `isAlive` / `readToken` are injected so the decision is pure and fully
184
+ * unit-testable without touching real processes.
185
+ */
186
+ export function classifyPid(meta, { readToken, isAlive }) {
187
+ if (meta == null || meta.pid == null) return "stale";
188
+ if (!isAlive(meta.pid)) return "stale";
189
+ if (meta.startToken == null) return "running-unverified";
190
+ const live = readToken(meta.pid);
191
+ if (live == null) return "running-unverified";
192
+ return live === meta.startToken ? "running-ours" : "stale";
193
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Cross-platform reader for the daemon's `apiToken` secret.
3
+ *
4
+ * CLI scripts (`run-now`, `remint-roadmap-ids`) need the daemon's apiToken to
5
+ * call Approve-tier `/api/...` routes, but they intentionally must run WITHOUT
6
+ * a successful `@aitne/shared` build (which only ships `./dist/*.js`). So this
7
+ * helper re-implements the per-OS read inline, mirroring the secret clients in
8
+ * `packages/shared/src/`:
9
+ *
10
+ * - darwin → macOS Keychain via `security` (service com.personal-agent.secret.apiToken)
11
+ * - win32 → DPAPI: decrypt ~/.personal-agent/secrets/apiToken.dpapi via PowerShell
12
+ * - linux → libsecret (`secret-tool lookup`), else the AES-256-GCM file store
13
+ * - WSL / other → the AES-256-GCM file store
14
+ *
15
+ * The file-store format/params and the DPAPI script are copied verbatim from
16
+ * `secret-client-file.ts` / `secret-client-windows.ts` — keep them in sync if
17
+ * those change. The secret clients hardcode `~/.personal-agent/secrets` (they
18
+ * do NOT honor PA_DATA_DIR), so this helper uses the same homedir-relative
19
+ * location to find what the daemon actually wrote.
20
+ *
21
+ * Returns the token string, or null when it cannot be read; the caller decides
22
+ * how to message the failure. Best-effort: never throws (so a CLI never dies
23
+ * with a raw stack trace on a misconfigured secret store).
24
+ */
25
+ import { execFileSync } from "node:child_process";
26
+ import { existsSync, readFileSync, statSync } from "node:fs";
27
+ import { homedir } from "node:os";
28
+ import { join } from "node:path";
29
+ import { createDecipheriv, scryptSync } from "node:crypto";
30
+
31
+ const SECRET_NAME = "apiToken";
32
+ const KEYCHAIN_SERVICE = "com.personal-agent.secret.apiToken";
33
+
34
+ function secretsDir() {
35
+ return join(homedir(), ".personal-agent", "secrets");
36
+ }
37
+
38
+ /** @returns {string | null} the daemon apiToken, or null if unreadable. */
39
+ export function readApiToken() {
40
+ const platform = process.platform;
41
+ if (platform === "darwin") return readDarwin();
42
+ if (platform === "win32") return readWindows();
43
+ if (platform === "linux") {
44
+ if (!isWsl() && whichSync("secret-tool")) {
45
+ const fromKeyring = readSecretTool();
46
+ if (fromKeyring) return fromKeyring;
47
+ }
48
+ return readFileStore();
49
+ }
50
+ return readFileStore();
51
+ }
52
+
53
+ function readDarwin() {
54
+ // Byte-identical to the historical `security` read so macOS cannot regress.
55
+ try {
56
+ return execFileSync(
57
+ "security",
58
+ ["find-generic-password", "-s", KEYCHAIN_SERVICE, "-w"],
59
+ { encoding: "utf-8", timeout: 5_000 },
60
+ ).trim();
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+
66
+ /** Cross-platform `which`, returning the resolved path or null. */
67
+ function whichSync(cmd) {
68
+ const tool = process.platform === "win32" ? "where" : "which";
69
+ try {
70
+ const out = execFileSync(tool, [cmd], {
71
+ stdio: ["ignore", "pipe", "ignore"],
72
+ timeout: 2_000,
73
+ });
74
+ return out.toString().split(/\r?\n/)[0]?.trim() || null;
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ function readWindows() {
81
+ const path = join(secretsDir(), `${SECRET_NAME}.dpapi`);
82
+ if (!existsSync(path)) return null;
83
+ const encrypted = readFileSync(path, "utf-8").trim();
84
+ // Prefer in-box Windows PowerShell 5.1, fall back to PowerShell 7+ (pwsh),
85
+ // matching the daemon's secret-client-factory resolution order.
86
+ const psBinary = whichSync("powershell.exe")
87
+ ? "powershell.exe"
88
+ : whichSync("pwsh.exe")
89
+ ? "pwsh.exe"
90
+ : "powershell.exe";
91
+ // Mirrors WindowsDpapiSecretClient.get(): DPAPI-decrypt via
92
+ // ConvertTo-SecureString and marshal back to plaintext. The ciphertext is
93
+ // passed via stdin, never interpolated into the script, to avoid injection.
94
+ const script = [
95
+ "$enc = [System.Console]::In.ReadToEnd().Trim()",
96
+ "$ss = ConvertTo-SecureString $enc",
97
+ "$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ss)",
98
+ "try { [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) }",
99
+ "finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }",
100
+ ].join("; ");
101
+ try {
102
+ const out = execFileSync(
103
+ psBinary,
104
+ ["-NoProfile", "-NonInteractive", "-Command", script],
105
+ { input: encrypted, encoding: "utf-8", timeout: 10_000 },
106
+ );
107
+ return out.trimEnd() || null;
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+
113
+ function readSecretTool() {
114
+ try {
115
+ const out = execFileSync(
116
+ "secret-tool",
117
+ ["lookup", "service", "personal-agent", "key", SECRET_NAME],
118
+ { encoding: "utf-8", timeout: 5_000 },
119
+ );
120
+ return out.replace(/\n$/, "") || null;
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Detect WSL. WSL reports platform "linux" but cannot use `secret-tool`
128
+ * (D-Bus / GNOME Keyring typically unavailable), so it uses the file store.
129
+ */
130
+ function isWsl() {
131
+ try {
132
+ return /microsoft|wsl/i.test(readFileSync("/proc/version", "utf-8"));
133
+ } catch {
134
+ return false;
135
+ }
136
+ }
137
+
138
+ // ── AES-256-GCM file store (mirrors secret-client-file.ts) ────────────────
139
+ const FILE_ALGORITHM = "aes-256-gcm";
140
+ const FILE_KEY_LENGTH = 32; // 256 bits
141
+ const FILE_SCRYPT = { N: 16384, r: 8, p: 1 };
142
+
143
+ /** Resolve the file-store master password (env, then key file); null if none. */
144
+ function resolveMasterPassword() {
145
+ if (process.env.PA_MASTER_PASSWORD) return process.env.PA_MASTER_PASSWORD;
146
+ const keyFilePath = join(secretsDir(), ".master-key");
147
+ if (!existsSync(keyFilePath)) return null;
148
+ // Refuse to read a key file with insecure permissions (mirrors the daemon's
149
+ // 0600/0400 gate); degrade to null rather than risk exposing it.
150
+ const mode = statSync(keyFilePath).mode & 0o777;
151
+ if (mode !== 0o600 && mode !== 0o400) return null;
152
+ return readFileSync(keyFilePath, "utf-8").trim();
153
+ }
154
+
155
+ function readFileStore() {
156
+ const path = join(secretsDir(), `${SECRET_NAME}.enc`);
157
+ if (!existsSync(path)) return null;
158
+ try {
159
+ const password = resolveMasterPassword();
160
+ if (!password) return null;
161
+ const stored = JSON.parse(readFileSync(path, "utf-8"));
162
+ const salt = Buffer.from(stored.salt, "hex");
163
+ const iv = Buffer.from(stored.iv, "hex");
164
+ const authTag = Buffer.from(stored.authTag, "hex");
165
+ const ciphertext = Buffer.from(stored.ciphertext, "hex");
166
+ const key = scryptSync(password, salt, FILE_KEY_LENGTH, FILE_SCRYPT);
167
+ const decipher = createDecipheriv(FILE_ALGORITHM, key, iv);
168
+ decipher.setAuthTag(authTag);
169
+ return Buffer.concat([
170
+ decipher.update(ciphertext),
171
+ decipher.final(),
172
+ ]).toString("utf-8");
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
package/scripts/start.mjs CHANGED
@@ -6,6 +6,7 @@ import path from "node:path";
6
6
  import process from "node:process";
7
7
  import { ensureBuild, log } from "./run-node.mjs";
8
8
  import { openBrowser, waitForHttpReady } from "./browser.mjs";
9
+ import { resolveDashboardPort } from "./lib/ports.mjs";
9
10
 
10
11
  const IS_WINDOWS = process.platform === "win32";
11
12
  const requireFromScript = createRequire(import.meta.url);
@@ -57,7 +58,7 @@ function nextSpawnArgs(dashboardDir, nextBin, userArgs) {
57
58
  * 5. Ctrl+C stops both
58
59
  */
59
60
 
60
- const DASHBOARD_PORT = parseInt(process.env.PA_DASHBOARD_PORT || "3000", 10);
61
+ const DASHBOARD_PORT = resolveDashboardPort();
61
62
  const noOpen = process.argv.slice(2).includes("--no-open");
62
63
  const children = [];
63
64
  let shuttingDown = false;
@@ -92,21 +93,31 @@ try {
92
93
  }
93
94
  const nextBin = resolveNextBin(dashboardDir);
94
95
  const dashArgs = nextSpawnArgs(dashboardDir, nextBin, [
95
- "dev", "--port", String(DASHBOARD_PORT),
96
+ // Bind IPv4 loopback explicitly: with no --hostname, Next binds the
97
+ // unspecified IPv6 address `::`, and on Windows (IPV6_V6ONLY on) the
98
+ // 127.0.0.1 readiness probe in bin/aitne.mjs can't reach it. Match the
99
+ // probe and the daemon's 127.0.0.1-only posture. Byte-identical on
100
+ // macOS/Linux where the probe already reaches a `::`-bound socket.
101
+ "dev", "--port", String(DASHBOARD_PORT), "--hostname", "127.0.0.1",
96
102
  ]);
97
- const dashboard = spawn(nextBin, dashArgs, {
103
+ const useShimShell = IS_WINDOWS && nextBin.toLowerCase().endsWith(".cmd");
104
+ const dashCommand = useShimShell ? `"${nextBin}"` : nextBin;
105
+ const dashboard = spawn(dashCommand, dashArgs, {
98
106
  cwd: dashboardDir,
99
107
  env: process.env,
100
108
  stdio: "inherit",
101
109
  windowsHide: true,
102
- shell: IS_WINDOWS && nextBin.toLowerCase().endsWith(".cmd"),
110
+ shell: useShimShell,
103
111
  });
104
112
  children.push(dashboard);
105
113
 
106
114
  // ── 4. Auto-open browser ──
107
115
 
108
116
  if (!noOpen) {
109
- const url = `http://localhost:${DASHBOARD_PORT}`;
117
+ // Match the dashboard's 127.0.0.1-only bind above. Using `localhost` here
118
+ // resolves to `::1` first on Windows, so both the readiness probe AND the
119
+ // browser open would miss the IPv4-only socket and time out.
120
+ const url = `http://127.0.0.1:${DASHBOARD_PORT}`;
110
121
  waitForHttpReady(url, {
111
122
  // `next dev` can take ~30s on a cold boot; give it headroom.
112
123
  timeoutMs: 60_000,
@@ -1,58 +0,0 @@
1
- ---
2
- schema_version: 1
3
- slug: features/lifestyle/travel-time
4
- title: Travel Time
5
- id: travel-time
6
- aliases:
7
- - door to door
8
- - eta
9
- category: features
10
- summary: |
11
- A skill that estimates door-to-door travel time given an origin
12
- and destination — used by schedule-approaching reminders for
13
- events with location.
14
- section: lifestyle
15
- tags:
16
- - lifestyle
17
- - travel
18
- - skills
19
- status: stable
20
- ask_examples:
21
- - How long will it take me to get to the airport?
22
- - Does the agent know about traffic?
23
- locale: en-US
24
- created: 2026-04-25
25
- updated: 2026-04-25
26
- keywords:
27
- - travel time
28
- - departure time
29
- - google maps
30
- - commute
31
- - ETA
32
- related:
33
- - features/lifestyle/travel-bookings
34
- - features/operations/schedule-approaching
35
- ---
36
-
37
- # Travel Time
38
-
39
- ## In One Sentence
40
-
41
- A skill the agent calls to estimate door-to-door travel time before
42
- a calendar event with a location.
43
-
44
- ## What It Does
45
-
46
- - Reads origin (current location, configured home/work) and
47
- destination (event location).
48
- - Returns a typical-time estimate with mode (drive, transit, walk).
49
- - Used by `schedule-approaching` reminders to lead-time the alert.
50
-
51
- ## Where in the Dashboard
52
-
53
- There is no operator surface for the travel-time data itself; the
54
- estimates appear inline in event reminders and morning routines.
55
-
56
- ## Related
57
-
58
- - [Schedule Approaching](../operations/schedule-approaching.md)
@@ -1,59 +0,0 @@
1
- ---
2
- kind: reference
3
- name: travel-time-api
4
- description: /api/travel-time reference — Google Maps Directions wrapper. Estimate door-to-door duration and compute departure time for a calendar event with location.
5
- ---
6
-
7
- # `/api/travel-time` reference
8
-
9
- Uses the Google Maps Directions API. Prerequisite: `googleMapsApiKey`
10
- configured in the daemon's secret store, with the Directions API
11
- enabled.
12
-
13
- ## GET /api/travel-time
14
-
15
- Estimate travel time between two locations.
16
-
17
- ```bash
18
- # Transit (default)
19
- curl -s "http://localhost:8321/api/travel-time?origin=Grand+Central&destination=Times+Square"
20
-
21
- # Driving with arrival time
22
- curl -s "http://localhost:8321/api/travel-time?origin=Brooklyn&destination=Newark&mode=driving&arrival=2026-04-12T14:00:00-04:00"
23
- ```
24
-
25
- | Param | Type | Default | Description |
26
- |-------|------|---------|-------------|
27
- | `origin` | string | (required) | Origin address or place name |
28
- | `destination` | string | (required) | Destination address or place name |
29
- | `mode` | string | transit | driving, transit, walking, bicycling |
30
- | `arrival` | ISO 8601 | — | Desired arrival time (computes departure time) |
31
-
32
- Response:
33
-
34
- ```json
35
- {
36
- "origin": "Grand Central Terminal, NY",
37
- "destination": "Times Square, NY",
38
- "mode": "transit",
39
- "durationSeconds": 1380,
40
- "durationText": "23 mins",
41
- "distanceMeters": 8500,
42
- "distanceText": "8.5 km",
43
- "departBy": "2026-04-12T13:34:00.000Z"
44
- }
45
- ```
46
-
47
- ## GET /api/travel-time/for-event/:eventId
48
-
49
- ```bash
50
- curl -s "http://localhost:8321/api/travel-time/for-event/abc123?origin=Home&mode=transit"
51
- ```
52
-
53
- | Param | Type | Default | Description |
54
- |-------|------|---------|-------------|
55
- | `origin` | string | (required) | Your starting location |
56
- | `mode` | string | transit | Travel mode |
57
-
58
- Response includes both `event` and `travelTime` blocks; see the route
59
- implementation for full shape.