things-api 0.18.0 → 0.19.1

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 (224) hide show
  1. package/README.md +18 -9
  2. package/deputy/AppIcon.icns +0 -0
  3. package/deputy/VERSION +1 -1
  4. package/deputy/helpers-Info.plist +4 -0
  5. package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
  6. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Info.plist +3 -1
  7. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
  8. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Resources/AppIcon.icns +0 -0
  9. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/_CodeSignature/CodeResources +15 -2
  10. package/deputy/prebuilt/Things API Helper.app/Contents/Info.plist +5 -1
  11. package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
  12. package/deputy/prebuilt/Things API Helper.app/Contents/Resources/AppIcon.icns +0 -0
  13. package/deputy/prebuilt/Things API Helper.app/Contents/_CodeSignature/CodeResources +14 -2
  14. package/deputy/reader/Info.plist +2 -0
  15. package/deputy/reader/entitlements.plist +30 -0
  16. package/deputy/reader/main.swift +129 -40
  17. package/deputy/src/server.swift +25 -2
  18. package/deputy/src/tcc.swift +58 -0
  19. package/dist/audit/schema.d.ts +30 -1
  20. package/dist/audit/schema.js.map +1 -1
  21. package/dist/capability.d.ts +322 -0
  22. package/dist/capability.js +706 -0
  23. package/dist/capability.js.map +1 -0
  24. package/dist/cli/commands/doctor.d.ts +13 -1
  25. package/dist/cli/commands/doctor.js +141 -5
  26. package/dist/cli/commands/doctor.js.map +1 -1
  27. package/dist/cli/commands/helpers.js +189 -51
  28. package/dist/cli/commands/helpers.js.map +1 -1
  29. package/dist/cli/commands/mcp.js +8 -0
  30. package/dist/cli/commands/mcp.js.map +1 -1
  31. package/dist/cli/commands/op-result.js +6 -0
  32. package/dist/cli/commands/op-result.js.map +1 -1
  33. package/dist/cli/commands/reads.js +47 -1
  34. package/dist/cli/commands/reads.js.map +1 -1
  35. package/dist/cli/commands/repeat-flags.js +34 -2
  36. package/dist/cli/commands/repeat-flags.js.map +1 -1
  37. package/dist/cli/commands/setup.d.ts +9 -0
  38. package/dist/cli/commands/setup.js +76 -83
  39. package/dist/cli/commands/setup.js.map +1 -1
  40. package/dist/cli/commands/writes.d.ts +1 -1
  41. package/dist/cli/commands/writes.js +311 -142
  42. package/dist/cli/commands/writes.js.map +1 -1
  43. package/dist/cli/dry-run.d.ts +1 -1
  44. package/dist/cli/dry-run.js +1 -1
  45. package/dist/cli/glyphs.d.ts +23 -4
  46. package/dist/cli/glyphs.js +46 -19
  47. package/dist/cli/glyphs.js.map +1 -1
  48. package/dist/cli/help.d.ts +1 -1
  49. package/dist/cli/help.js +49 -27
  50. package/dist/cli/help.js.map +1 -1
  51. package/dist/cli/interrupt.d.ts +54 -6
  52. package/dist/cli/interrupt.js +78 -12
  53. package/dist/cli/interrupt.js.map +1 -1
  54. package/dist/cli/main.js +8 -5
  55. package/dist/cli/main.js.map +1 -1
  56. package/dist/cli/period.d.ts +3 -1
  57. package/dist/cli/period.js +1 -1
  58. package/dist/cli/period.js.map +1 -1
  59. package/dist/cli/read-driver.js +26 -1
  60. package/dist/cli/read-driver.js.map +1 -1
  61. package/dist/cli/render.d.ts +31 -4
  62. package/dist/cli/render.js +127 -15
  63. package/dist/cli/render.js.map +1 -1
  64. package/dist/client.d.ts +23 -3
  65. package/dist/client.js +36 -13
  66. package/dist/client.js.map +1 -1
  67. package/dist/contracts.d.ts +8 -4
  68. package/dist/contracts.js +1 -1
  69. package/dist/contracts.js.map +1 -1
  70. package/dist/deputy/install.d.ts +234 -6
  71. package/dist/deputy/install.js +843 -31
  72. package/dist/deputy/install.js.map +1 -1
  73. package/dist/deputy/notices.js +2 -2
  74. package/dist/deputy/notices.js.map +1 -1
  75. package/dist/deputy/protocol.d.ts +59 -5
  76. package/dist/deputy/protocol.js +48 -11
  77. package/dist/deputy/protocol.js.map +1 -1
  78. package/dist/deputy/routing.d.ts +37 -0
  79. package/dist/deputy/routing.js +181 -27
  80. package/dist/deputy/routing.js.map +1 -1
  81. package/dist/deputy/wake.d.ts +61 -0
  82. package/dist/deputy/wake.js +149 -0
  83. package/dist/deputy/wake.js.map +1 -0
  84. package/dist/diagnose.d.ts +19 -4
  85. package/dist/diagnose.js +64 -8
  86. package/dist/diagnose.js.map +1 -1
  87. package/dist/direct-setup.d.ts +104 -0
  88. package/dist/direct-setup.js +658 -0
  89. package/dist/direct-setup.js.map +1 -0
  90. package/dist/host-access.d.ts +75 -0
  91. package/dist/host-access.js +177 -0
  92. package/dist/host-access.js.map +1 -0
  93. package/dist/index.d.ts +20 -5
  94. package/dist/index.js +14 -1
  95. package/dist/index.js.map +1 -1
  96. package/dist/mcp/server.d.ts +27 -1
  97. package/dist/mcp/server.js +308 -109
  98. package/dist/mcp/server.js.map +1 -1
  99. package/dist/op-result.d.ts +9 -0
  100. package/dist/op-result.js +13 -1
  101. package/dist/op-result.js.map +1 -1
  102. package/dist/read/filter-contract.d.ts +1 -1
  103. package/dist/read/filter-contract.js +4 -0
  104. package/dist/read/filter-contract.js.map +1 -1
  105. package/dist/read/shape.js +4 -0
  106. package/dist/read/shape.js.map +1 -1
  107. package/dist/read/views.d.ts +29 -0
  108. package/dist/read/views.js +60 -0
  109. package/dist/read/views.js.map +1 -1
  110. package/dist/session-grant.d.ts +48 -0
  111. package/dist/session-grant.js +185 -0
  112. package/dist/session-grant.js.map +1 -0
  113. package/dist/surface-copy.d.ts +19 -3
  114. package/dist/surface-copy.js +19 -3
  115. package/dist/surface-copy.js.map +1 -1
  116. package/dist/wizard.d.ts +58 -0
  117. package/dist/wizard.js +174 -0
  118. package/dist/wizard.js.map +1 -0
  119. package/dist/write/availability.d.ts +1 -16
  120. package/dist/write/availability.js +10 -65
  121. package/dist/write/availability.js.map +1 -1
  122. package/dist/write/batch.d.ts +20 -0
  123. package/dist/write/batch.js +111 -21
  124. package/dist/write/batch.js.map +1 -1
  125. package/dist/write/capabilities.d.ts +9 -0
  126. package/dist/write/capabilities.js +2 -0
  127. package/dist/write/capabilities.js.map +1 -1
  128. package/dist/write/clear-reminder.js +1 -1
  129. package/dist/write/clear-reminder.js.map +1 -1
  130. package/dist/write/commands.d.ts +22 -7
  131. package/dist/write/commands.js +231 -254
  132. package/dist/write/commands.js.map +1 -1
  133. package/dist/write/failure-hints.d.ts +32 -7
  134. package/dist/write/failure-hints.js +9 -14
  135. package/dist/write/failure-hints.js.map +1 -1
  136. package/dist/write/guards.js +22 -4
  137. package/dist/write/guards.js.map +1 -1
  138. package/dist/write/lock.d.ts +16 -13
  139. package/dist/write/lock.js +55 -0
  140. package/dist/write/lock.js.map +1 -1
  141. package/dist/write/make-repeating-project.js +9 -13
  142. package/dist/write/make-repeating-project.js.map +1 -1
  143. package/dist/write/move.js +9 -12
  144. package/dist/write/move.js.map +1 -1
  145. package/dist/write/operations.d.ts +51 -29
  146. package/dist/write/operations.js +16 -0
  147. package/dist/write/operations.js.map +1 -1
  148. package/dist/write/opid.d.ts +72 -14
  149. package/dist/write/opid.js +152 -8
  150. package/dist/write/opid.js.map +1 -1
  151. package/dist/write/param-schema.d.ts +80 -0
  152. package/dist/write/param-schema.js +754 -0
  153. package/dist/write/param-schema.js.map +1 -0
  154. package/dist/write/pipeline.d.ts +110 -5
  155. package/dist/write/pipeline.js +288 -14
  156. package/dist/write/pipeline.js.map +1 -1
  157. package/dist/write/pre-state.d.ts +38 -16
  158. package/dist/write/pre-state.js +117 -22
  159. package/dist/write/pre-state.js.map +1 -1
  160. package/dist/write/promote-clone.js +658 -444
  161. package/dist/write/promote-clone.js.map +1 -1
  162. package/dist/write/reorder.js +25 -8
  163. package/dist/write/reorder.js.map +1 -1
  164. package/dist/write/repeat-collateral.d.ts +31 -0
  165. package/dist/write/repeat-collateral.js +153 -0
  166. package/dist/write/repeat-collateral.js.map +1 -0
  167. package/dist/write/repeat-rule.d.ts +28 -1
  168. package/dist/write/repeat-rule.js +54 -4
  169. package/dist/write/repeat-rule.js.map +1 -1
  170. package/dist/write/resolution-timestamps.d.ts +11 -0
  171. package/dist/write/resolution-timestamps.js +108 -40
  172. package/dist/write/resolution-timestamps.js.map +1 -1
  173. package/dist/write/reversibility.js +4 -0
  174. package/dist/write/reversibility.js.map +1 -1
  175. package/dist/write/template-mutation.d.ts +137 -0
  176. package/dist/write/template-mutation.js +598 -0
  177. package/dist/write/template-mutation.js.map +1 -0
  178. package/dist/write/undo.js +10 -20
  179. package/dist/write/undo.js.map +1 -1
  180. package/dist/write/update-fields.d.ts +151 -0
  181. package/dist/write/update-fields.js +427 -0
  182. package/dist/write/update-fields.js.map +1 -0
  183. package/dist/write/vectors/applescript.js +6 -10
  184. package/dist/write/vectors/applescript.js.map +1 -1
  185. package/dist/write/vectors/registry.d.ts +1 -1
  186. package/dist/write/vectors/registry.js +10 -2
  187. package/dist/write/vectors/registry.js.map +1 -1
  188. package/dist/write/vectors/shortcuts.js +2 -2
  189. package/dist/write/vectors/shortcuts.js.map +1 -1
  190. package/dist/write/vectors/simulator.js +15 -4
  191. package/dist/write/vectors/simulator.js.map +1 -1
  192. package/dist/write/vectors/types.d.ts +268 -1
  193. package/dist/write/vectors/ui-certification.js +63 -13
  194. package/dist/write/vectors/ui-certification.js.map +1 -1
  195. package/dist/write/vectors/ui-chord.d.ts +144 -0
  196. package/dist/write/vectors/ui-chord.js +428 -0
  197. package/dist/write/vectors/ui-chord.js.map +1 -0
  198. package/dist/write/vectors/ui-drag.d.ts +8 -4
  199. package/dist/write/vectors/ui-drag.js +2 -0
  200. package/dist/write/vectors/ui-drag.js.map +1 -1
  201. package/dist/write/vectors/ui-recipes.d.ts +70 -3
  202. package/dist/write/vectors/ui-recipes.js +482 -44
  203. package/dist/write/vectors/ui-recipes.js.map +1 -1
  204. package/dist/write/vectors/ui.d.ts +280 -10
  205. package/dist/write/vectors/ui.js +1068 -77
  206. package/dist/write/vectors/ui.js.map +1 -1
  207. package/dist/write/vectors/url-scheme.js +3 -0
  208. package/dist/write/vectors/url-scheme.js.map +1 -1
  209. package/dist/write/verify/delta.d.ts +62 -0
  210. package/dist/write/verify/delta.js +36 -2
  211. package/dist/write/verify/delta.js.map +1 -1
  212. package/dist/write/verify/poller.d.ts +4 -1
  213. package/dist/write/verify/poller.js +14 -0
  214. package/dist/write/verify/poller.js.map +1 -1
  215. package/package.json +2 -1
  216. package/schema/envelope.schema.json +6 -4
  217. package/scripts/build-helpers.sh +5 -0
  218. package/skills/things-cli/SKILL.md +5 -3
  219. package/skills/things-cli/references/contracts.md +1 -0
  220. package/skills/things-cli/references/data-model.md +3 -0
  221. package/skills/things-cli/references/errors.md +1 -1
  222. package/skills/things-cli/references/gui.md +1 -0
  223. package/skills/things-cli/references/repeating.md +6 -0
  224. package/skills/things-cli/references/tag-cleanup.md +64 -0
@@ -1,3 +1,4 @@
1
+ import type { DeltaSpec, OccurrenceResolution } from "../write/verify/delta.ts";
1
2
  export interface AuditRecord {
2
3
  v: 1;
3
4
  ts: string;
@@ -41,13 +42,41 @@ export interface AuditRecord {
41
42
  * bumped) without a separate read. Absent on writes made without the flag.
42
43
  */
43
44
  preModDates?: Record<string, number | null>;
45
+ /**
46
+ * Template-target composite disclosure (ADDITIVE): which occurrence a
47
+ * `complete`/`cancel`/`update --exception` aimed at a repeating series
48
+ * actually wrote, and whether the composite minted it. Recorded on the
49
+ * composite's SUMMARY record only — the one record its `opId` keys — so an
50
+ * idempotency replay can hand the caller the same two uuids the original
51
+ * call returned without re-reading the database.
52
+ */
53
+ occurrence?: OccurrenceResolution;
54
+ /**
55
+ * AMBIGUOUS-OUTCOME reconciliation key (ADDITIVE): the expected-state
56
+ * assertion this attempt was verifying, recorded on `verify-failed:timeout`
57
+ * records only — the one result class where the change may or may not have
58
+ * landed. A resubmission carrying the same `opId` re-evaluates THIS assertion
59
+ * against current state to decide whether the timed-out change is there
60
+ * (replay it as already-applied) or absent (execute normally), so the presence
61
+ * test is the attempt's OWN oracle rather than a per-operation guess
62
+ * (`src/write/opid.ts`). Absent on every other record — and an absent one is a
63
+ * refusal to guess, never an assumption either way.
64
+ */
65
+ expected?: DeltaSpec;
44
66
  /** Normalized requested delta (params as given, post-normalization). */
45
67
  requested: Record<string, unknown>;
46
68
  /** Asserted-field subset of the pre-state (null when target didn't exist). */
47
69
  pre: Record<string, unknown> | null;
48
70
  /** Post-verify observation (best-effort on failure). */
49
71
  observed: Record<string, unknown> | null;
50
- result: "intent" | "ok" | "verify-failed:timeout" | "verify-failed:mismatch" | "verify-failed:silent-noop" | `blocked:${string}` | "unsupported";
72
+ result: "intent" | "ok" | "verify-failed:timeout" | "verify-failed:mismatch" | "verify-failed:silent-noop"
73
+ /** A GUI drive stopped because the Things window was unreachable/unresponsive (#512). */
74
+ | "verify-failed:ui-unreachable"
75
+ /**
76
+ * The requested change landed, but a field the caller never named moved with it
77
+ * and nothing in the operation's vocabulary attributes the movement (CGRD1).
78
+ */
79
+ | "verify-failed:collateral" | `blocked:${string}` | "unsupported";
51
80
  verify: {
52
81
  attempts: number;
53
82
  elapsedMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/audit/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiEzC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CACvB,MAA0E;IAE1E,IAAI,MAAM,CAAC,GAAG,EAAE,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACzD,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,MAAM,CAAC,EAAE;QACT,MAAM,CAAC,EAAE;QACT,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,IAAI;QACX,MAAM,CAAC,IAAI,IAAI,EAAE;KAClB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChF,OAAO,KAAK,MAAM,EAAE,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/audit/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+FzC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CACvB,MAA0E;IAE1E,IAAI,MAAM,CAAC,GAAG,EAAE,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACzD,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,MAAM,CAAC,EAAE;QACT,MAAM,CAAC,EAAE;QACT,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,IAAI;QACX,MAAM,CAAC,IAAI,IAAI,EAAE;KAClB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChF,OAAO,KAAK,MAAM,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,322 @@
1
+ import { type TargetWake } from "./deputy/wake.ts";
2
+ import { type HostAccessDeps, type HostApp } from "./host-access.ts";
3
+ export { fdaGranted, type FdaVerdict, type HostApp, hostApp, hostDisplayName, tccDbPath, } from "./host-access.ts";
4
+ /** The Things application's bundle identifier — the Automation grant's target. */
5
+ export { THINGS_BUNDLE_ID } from "./deputy/wake.ts";
6
+ /** How reads may reach the live library, if at all. */
7
+ export type ReadCapabilityMode =
8
+ /** A caller-supplied database path — outside the doctrine entirely (Article VI). */
9
+ "explicit-db"
10
+ /** Reads ride the sandboxed reader; the read itself is the capability check. */
11
+ | "helpers"
12
+ /** The helpers are expected here but cannot serve — refuse, never fall back. */
13
+ | "helpers-unavailable"
14
+ /** No helpers, but the host app holds Full Disk Access. */
15
+ | "direct-fda"
16
+ /** A ceremony witnessed an app-data grant that is still live for this app instance. */
17
+ | "session-grant"
18
+ /** Nothing can open the container — the caller must run a setup ceremony. */
19
+ | "none";
20
+ /** How GUI-driving may be delivered, if at all (Article IV). */
21
+ export type UiCapabilityMode =
22
+ /** The helper pair holds Accessibility + Automation→System Events. */
23
+ "helpers"
24
+ /** The lab's documented in-guest escape (see {@link UI_DIRECT_ESCAPE_ENV}). */
25
+ | "direct-escape"
26
+ /** GUI-driving is switched off in config — nothing has been asked for yet. */
27
+ | "config-disabled"
28
+ /** No deputy answers, so there is no identity that could hold the grants. */
29
+ | "helpers-missing"
30
+ /** System Events is down and would not start — a liveness fault, not a grant one. */
31
+ | "target-unreachable"
32
+ /** The deputy answers but the `--gui` tier is incomplete. */
33
+ | "tier-incomplete";
34
+ /**
35
+ * Where Things' OWN in-app authorization for the URL scheme stands (URLEN1).
36
+ *
37
+ * This is not a macOS consent class at all — `open -g things:///…` is a
38
+ * LaunchServices dispatch that needs no grant. The gate is the app's: Settings
39
+ * ▸ General ▸ "Enable Things URLs", recorded as `uriSchemeEnabled` in the
40
+ * group-container preferences plist. Three states, all measured.
41
+ */
42
+ export type UrlSchemeCapabilityMode =
43
+ /** `uriSchemeEnabled = 1` — URL commands execute. */
44
+ "enabled"
45
+ /** `uriSchemeEnabled = 0` — the app drops URL MUTATIONS with no dialog at all. */
46
+ | "disabled"
47
+ /** No key: nobody has answered the app's first-use dialog on this machine. */
48
+ | "never-asked"
49
+ /** The plist cannot be reached prompt-free — never resolved by dispatching. */
50
+ | "unreadable";
51
+ /** How app automation may be delivered, if at all. */
52
+ export type WriteCapabilityMode =
53
+ /** The deputy is onboarded; Apple Events are sent under the helper identity. */
54
+ "deputy"
55
+ /** Things is not running, so where the deputy's grant stands cannot be read. */
56
+ | "deputy-target-dormant"
57
+ /** macOS records an Automation grant for the host app against Things. */
58
+ | "direct-granted"
59
+ /** The lab's documented in-guest escape (see {@link WRITE_DIRECT_ESCAPE_ENV}). */
60
+ | "direct-escape"
61
+ /** macOS records a REFUSAL. It will not re-ask; the human must re-arm it. */
62
+ | "direct-denied"
63
+ /** No record either way, or none readable — never resolved by prompting. */
64
+ | "direct-unknown";
65
+ export interface Capability<Mode> {
66
+ mode: Mode;
67
+ /** One sentence of provenance, for `doctor` and for refusal copy. */
68
+ detail: string;
69
+ /**
70
+ * The remediation lines a refusal prints, in the order they should be
71
+ * offered. Empty when the capability is present.
72
+ */
73
+ remediation: string[];
74
+ /** The identity a direct-mode grant would attach to. */
75
+ host: HostApp;
76
+ }
77
+ export type ReadCapability = Capability<ReadCapabilityMode>;
78
+ export type WriteCapability = Capability<WriteCapabilityMode>;
79
+ export type UiCapability = Capability<UiCapabilityMode>;
80
+ export type UrlSchemeCapability = Capability<UrlSchemeCapabilityMode>;
81
+ /** True when this verdict permits opening the live container. */
82
+ export declare function readAllowed(capability: ReadCapability): boolean;
83
+ /** True when this verdict permits driving the Things window. */
84
+ export declare function uiAllowed(capability: UiCapability): boolean;
85
+ /**
86
+ * True when a URL-scheme MUTATION may be dispatched.
87
+ *
88
+ * `unreadable` is permissive, and deliberately so — the asymmetry with the
89
+ * write gate is the point. There, an unknown standing is refused because
90
+ * resolving it means sending the Apple Event that IS the dialog. Here,
91
+ * dispatching costs nothing on a machine whose answer is already "enabled",
92
+ * which is every settled machine; only the two states we have positively READ
93
+ * as not-enabled are refused. What catches the unreadable case instead is the
94
+ * read-after-write verify plus its likely-cause hint (src/write/failure-hints.ts).
95
+ */
96
+ export declare function urlSchemeAllowed(capability: UrlSchemeCapability): boolean;
97
+ /** True when app automation may be dispatched. */
98
+ export declare function writeAllowed(capability: WriteCapability): boolean;
99
+ /**
100
+ * Injection seams. Every one of these defaults to a real, prompt-free probe;
101
+ * tests replace them so no test ever touches the host's TCC state.
102
+ */
103
+ export interface CapabilityDeps extends HostAccessDeps {
104
+ /** Are this process's database reads actually being served by the reader? */
105
+ helpersServing?: () => boolean;
106
+ /** Are the helpers expected on this machine (enabled, and installed under auto)? */
107
+ helpersExpected?: () => boolean;
108
+ /** Why the helpers are not serving, for the loud refusal. */
109
+ helpersReason?: () => string | null;
110
+ /** The deputy's `automation.things` standing, from its handshake. */
111
+ deputyAutomation?: () => string | undefined;
112
+ /**
113
+ * The deputy's GUI-driving standing, from the same handshake: whether it is
114
+ * Accessibility-trusted and where its Automation→System Events grant stands.
115
+ * `null` means no deputy answered at all.
116
+ */
117
+ deputyGuiStanding?: () => {
118
+ axTrusted: boolean | undefined;
119
+ systemEvents: string | undefined;
120
+ } | null;
121
+ /**
122
+ * Start System Events and re-read its Automation standing, for the one state
123
+ * where the handshake reports liveness instead of authorization
124
+ * (`not-running`). Prompt-free — see ./deputy/wake.ts for why the launch must
125
+ * come before the determination.
126
+ */
127
+ wakeSystemEvents?: () => TargetWake;
128
+ /**
129
+ * Start Things and re-read its Automation standing — the same liveness step
130
+ * for the AppleScript vector's own target (#617). Consulted ONLY on the
131
+ * dispatch path: a survey never launches the user's app.
132
+ */
133
+ wakeThings?: () => TargetWake;
134
+ /**
135
+ * Report a woken target's real standing back to the routing layer, which
136
+ * defers its `auto` onboarding gate while Things is closed
137
+ * (./deputy/routing.ts, {@link settleDeputyAutomation}).
138
+ */
139
+ settleDeputyAutomation?: (standing: string | undefined) => void;
140
+ /** Is GUI-driving switched on in config (`ui-enabled`)? */
141
+ uiEnabled?: () => boolean;
142
+ /**
143
+ * Read one Automation row out of TCC.db. Returns the raw `auth_value`, or
144
+ * null when there is no row / the file cannot be read.
145
+ */
146
+ automationAuthValue?: (client: string, target: string) => number | null;
147
+ /** Resolve a running app's display name from its bundle id (LaunchServices). */
148
+ lookupAppName?: (bundleId: string) => string | null;
149
+ /**
150
+ * The read standing {@link urlSchemeCapability} consults before it touches
151
+ * the group container. Defaults to a live {@link readCapability} call.
152
+ */
153
+ readStanding?: () => ReadCapability;
154
+ /** Raw bytes of the Things group-container preferences plist. Throws when unreachable. */
155
+ readPrefsPlist?: () => Buffer;
156
+ /** Pull `uriSchemeEnabled` out of those bytes. Throws when the key is absent. */
157
+ extractUriSchemeEnabled?: (plistBytes: Buffer) => string;
158
+ }
159
+ /**
160
+ * Can this process read the live Things library, and on whose authority?
161
+ *
162
+ * Stateless: every call re-derives the verdict, because a grant can appear or
163
+ * vanish between one invocation and the next and a cached "yes" would be a
164
+ * stored onboarding flag by another name. The common case costs one `open(2)`.
165
+ */
166
+ export declare function readCapability(options?: {
167
+ dbPath?: string;
168
+ }, deps?: CapabilityDeps): ReadCapability;
169
+ /** Thrown when a read is refused for want of capability (Article II). */
170
+ export declare class ReadCapabilityError extends Error {
171
+ readonly remediation: string[];
172
+ readonly capability: ReadCapability;
173
+ constructor(capability: ReadCapability);
174
+ }
175
+ /**
176
+ * The LAB's escape hatch for the AppleScript vector — the write-side twin of
177
+ * {@link UI_DIRECT_ESCAPE_ENV}, and just as deliberately not consumer surface.
178
+ *
179
+ * A guest shell in a golden clone descends from sshd, not from an application
180
+ * bundle, so `hostApp()` finds no `__CFBundleIdentifier` and macOS has no
181
+ * identity to have recorded an Automation grant against. The verdict is
182
+ * therefore `direct-unknown` in every clone, which blocks every AppleScript-
183
+ * vector verb and every composite carrying an AppleScript leg. What the clone
184
+ * actually has is an in-guest Automation grant on the runner's own processes
185
+ * (the same AXVM1 layer the ui escape leans on), so the block is an artefact of
186
+ * UNKNOWABILITY, not of a missing grant. Setting this to `1` says so.
187
+ *
188
+ * Bounded, and the bound is the point: it is consulted ONLY on the
189
+ * bundle-id-less path. A host that has an identity is answered from its TCC row
190
+ * as it always was, so the escape can never mask a recorded refusal
191
+ * (`direct-denied`) or manufacture a grant for a real user's terminal.
192
+ * Documented in docs/lab/harness.md and exported by the lab's guest
193
+ * environment; nothing consumer-facing mentions it.
194
+ */
195
+ export declare const WRITE_DIRECT_ESCAPE_ENV = "THINGS_API_WRITE_DIRECT";
196
+ /**
197
+ * What a {@link writeCapability} verdict is FOR — the one thing that decides
198
+ * whether a dormant Things may be started while the verdict is taken (#617).
199
+ *
200
+ * The asymmetry with the GUI preflight is deliberate. System Events is a
201
+ * headless macOS component nobody sees, so {@link uiCapability} wakes it for
202
+ * every caller. Things is the user's own app: starting it is visible, so only a
203
+ * caller that is ABOUT TO DRIVE IT may do so.
204
+ */
205
+ export interface WriteCapabilityOptions {
206
+ /**
207
+ * - `survey` (the default) — `doctor`, the MCP startup bake, `--dry-run`.
208
+ * Launches nothing and reports a closed Things as the liveness state
209
+ * `deputy-target-dormant`.
210
+ * - `dispatch` — the write gate and the two setup ceremonies, which are about
211
+ * to send Things an Apple Event anyway. A dormant target is started in the
212
+ * background first and its standing re-read, which is also what keeps the
213
+ * operation at tier 0/1: an Apple Event to a CLOSED Things auto-launches it
214
+ * WITH focus steal (A40/A41), a background pre-launch does not.
215
+ */
216
+ purpose?: "survey" | "dispatch";
217
+ }
218
+ /**
219
+ * May this process drive Things over Apple Events, and on whose authority?
220
+ *
221
+ * The deputy wins when it is onboarded (its own handshake reports the grant it
222
+ * holds). Otherwise the host app's own Automation record is read out of TCC —
223
+ * granted, denied, or, when there is no record at all, `direct-unknown`. That
224
+ * last state is deliberately NOT resolved here: resolving it means sending a
225
+ * real Apple Event, which is what raises the dialog, and Article I reserves
226
+ * that for `things setup`.
227
+ *
228
+ * LIVENESS BEFORE AUTHORIZATION (#617). The deputy's `not-running` is the
229
+ * ask-false determination having no answer for a CLOSED Things — a fact about
230
+ * the app's process, not about the grant. Two rules follow, and both matter:
231
+ *
232
+ * - while the deputy is standing, that value NEVER falls through to the direct
233
+ * host branch. A silent direct engagement would put consent back on the
234
+ * terminal (the routing doctrine's no-fallback rule), and on a machine with
235
+ * no host record it would refuse a fully onboarded user with "run
236
+ * `things setup`" — the #610 false-onboarding loop, one vector over;
237
+ * - a `dispatch` caller resolves it by STARTING Things (a background
238
+ * LaunchServices dispatch, never an Apple Event) and re-reading the
239
+ * standing. Only what comes back is an authorization fact.
240
+ */
241
+ export declare function writeCapability(options?: WriteCapabilityOptions, deps?: CapabilityDeps): WriteCapability;
242
+ /** Thrown when app automation is refused for want of capability (Article II). */
243
+ export declare class WriteCapabilityError extends Error {
244
+ readonly remediation: string[];
245
+ readonly capability: WriteCapability;
246
+ constructor(capability: WriteCapability);
247
+ }
248
+ /**
249
+ * The LAB's escape hatch for the ui vector, and deliberately not consumer
250
+ * surface (its write-side twin is {@link WRITE_DIRECT_ESCAPE_ENV}). The VM lab and
251
+ * the guest e2e bundle drive the UI vector DIRECT — the in-guest Accessibility
252
+ * grant is held by the runner's own processes (the AXVM1 layer), there is no
253
+ * helper bundle in a disposable clone, and there is nobody to answer a dialog
254
+ * either. Setting this to `1` restores direct UI-vector availability for that
255
+ * one situation. It is documented in docs/lab/harness.md and exported by the
256
+ * lab's guest environment; nothing consumer-facing mentions it, and it does not
257
+ * bypass `ui.enabled` — a lab clone still sets that key explicitly.
258
+ */
259
+ export declare const UI_DIRECT_ESCAPE_ENV = "THINGS_API_UI_DIRECT";
260
+ /**
261
+ * May this process drive the Things WINDOW, and on whose authority?
262
+ *
263
+ * Article IV admits exactly one provenance: the helper pair. Accessibility on a
264
+ * general-purpose host app (a terminal, an agent harness, an MCP host) has a
265
+ * blast radius far beyond Things, churns with every host update, and has no
266
+ * sane story at all over ssh — so direct AX is unsupported, and a refusal here
267
+ * names the config knob and `things helpers setup --gui` rather than raising an
268
+ * Accessibility prompt against whatever happens to be running us.
269
+ *
270
+ * Every answer is prompt-free: the config key is a file read, and the deputy's
271
+ * `hello` carries `AXIsProcessTrusted()` plus its own `AEDeterminePermission`
272
+ * verdict for System Events. The one state that needs more than a read is a
273
+ * DORMANT System Events, which macOS reaps whenever it has been idle: the
274
+ * target is started in the background — never by sending it an event — and the
275
+ * determination re-read, so `not-running` resolves to the truth instead of
276
+ * masquerading as a missing grant (./deputy/wake.ts).
277
+ */
278
+ export declare function uiCapability(deps?: CapabilityDeps): UiCapability;
279
+ /**
280
+ * Has Things been authorized to act on `things:///` commands, and how do we know?
281
+ *
282
+ * Stateless per invocation, like every other verdict here: the setting is the
283
+ * user's to flip at any moment, and a cached "yes" would be a stored onboarding
284
+ * flag by another name. The common case costs one file read plus one `plutil`.
285
+ *
286
+ * WHY THIS IS GATED ON THE READ STANDING. The plist lives inside the Things
287
+ * group container, which is the same `kTCCServiceSystemPolicyAppData` class as
288
+ * the database — so the open is itself what would raise the app-data modal on a
289
+ * machine that holds no standing. This function therefore never opens it
290
+ * speculatively: it asks {@link readCapability} first and reports `unreadable`
291
+ * unless the container is already reachable (helpers, FDA, or a live session
292
+ * grant). Where the helpers are serving, the read rides the reader's own
293
+ * security-scoped bookmark over the container, so the prefs plist is inside the
294
+ * granted subtree and no host grant is involved at all.
295
+ *
296
+ * MEASURED, all three states (URLEN1, golden-v4 / Things 3.23):
297
+ *
298
+ * - `1` — URL mutations execute.
299
+ * - `0` — every mutating verb (`add`, token-bearing `update`, the `json`
300
+ * batch) is dropped in TOTAL SILENCE: zero row delta, no dialog, no window
301
+ * of any kind, and nothing to wait for. Navigation URLs (`things:///show`)
302
+ * still work, so this gate covers mutations only.
303
+ * - absent — nobody has answered the app's own first-use "Things URL Scheme"
304
+ * dialog (Cancel / Enable). The dispatched request PARKS behind that dialog
305
+ * rather than being dropped, which is what #611 saw: with nobody at the
306
+ * machine, the verify window expired and the write reported a silent no-op.
307
+ */
308
+ export declare function urlSchemeCapability(deps?: CapabilityDeps): UrlSchemeCapability;
309
+ /** Thrown when GUI-driving is refused for want of capability (Article IV). */
310
+ export declare class UiCapabilityError extends Error {
311
+ readonly remediation: string[];
312
+ readonly capability: UiCapability;
313
+ constructor(capability: UiCapability);
314
+ }
315
+ /** Thrown when a URL-scheme mutation is refused for want of the app's own authorization. */
316
+ export declare class UrlSchemeCapabilityError extends Error {
317
+ readonly remediation: string[];
318
+ readonly capability: UrlSchemeCapability;
319
+ constructor(capability: UrlSchemeCapability);
320
+ }
321
+ /** Test seam: forget the one memo this module keeps (the host's display name). */
322
+ export declare function resetCapabilityForTests(): void;