@happyvertical/smrt-jobs 0.40.61 → 0.40.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -109,6 +109,32 @@ transitions and monotonic checkpoints in
109
109
 
110
110
  Mixin that adds `bg()` and `background()` to any SmrtObject. Uses WeakMap for collection caching per DB instance.
111
111
 
112
+ ## Background policy (`background-policy.ts`, S5 audit #1402)
113
+
114
+ Three opt-in guards, all owned **and enforced here**. Other packages apply the
115
+ eligibility marker (`reports`, `support`, `fields`, the MCP conformance
116
+ fixture), but nothing outside this package acts on it — `TaskRunner` is the only
117
+ reader. In particular `@happyvertical/smrt-agents` has no reference to any of
118
+ these guards and does not depend on this package, so marking a method does not
119
+ change what the agents runtime will dispatch.
120
+
121
+ - `clampRetries()` / `MAX_JOB_RETRIES` (25): a requested retry count above the
122
+ ceiling is clamped, not rejected.
123
+ - `assertWithinTenantCreationCap(tenantId, current, cap)` /
124
+ `DEFAULT_TENANT_JOB_CAP` (10 000): throws `TenantJobCapExceededError` when the
125
+ tenant's count of non-terminal jobs is already at or above the cap. Applied in
126
+ `SmrtJobCollection.enqueueJob()` (`smrt-job.ts`), which is the single choke
127
+ point for the builder and `ScheduleRunner`; a `null` tenant and a `cap <= 0`
128
+ both skip the check. Deliberately not serialized — concurrent enqueues can
129
+ overshoot by the number of in-flight creators (see the method's own comment).
130
+ - `@backgroundEligible()` / `markBackgroundEligible()` /
131
+ `isBackgroundEligibleMethod()`: an allowlist of methods `TaskRunner` may
132
+ dispatch, enforced at exactly one call site (`runner.ts`, after the method
133
+ lookup). **Restrictive, not enabling** — a class with no marked methods allows
134
+ any of its methods, and the first mark makes the set exhaustive, excluding
135
+ every sibling method. Adding the decorator to one method of an existing class
136
+ is therefore a behaviour change for the rest of it.
137
+
112
138
  ## Gotchas
113
139
 
114
140
  - **Cron not timezone-aware**: cron fields match the server's **local** time, not UTC (set `TZ` for UTC); no missed-run catch-up (fire-once-forward)
package/README.md CHANGED
@@ -117,6 +117,25 @@ runner.on('job:failed', (job, error) => { /* ... */ });
117
117
  process.on('SIGTERM', () => runner.stop());
118
118
  ```
119
119
 
120
+ ### Back MCP task operations with durable jobs
121
+
122
+ `McpTaskStore` persists the MCP `io.modelcontextprotocol/tasks` lifecycle on
123
+ the same `_smrt_jobs` row that executes the operation. `createTask()` creates a
124
+ correlated job, `getTask()` maps its queue state, and `cancelTask()` cancels
125
+ that exact job without leaving a second record behind. Long-running task
126
+ actions can request client input through `JobExecutionContext.task`:
127
+
128
+ ```typescript
129
+ async generate(options: Record<string, never>, context: JobExecutionContext) {
130
+ const { tone } = await context.task!.requestInput({ tone: { type: 'string' } });
131
+ return this.render(tone);
132
+ }
133
+ ```
134
+
135
+ Run a `TaskRunner` for the `mcp-tasks` queue in application deployments. Task
136
+ cancellation is cooperative: the job row becomes cancelled immediately and a
137
+ running handler must observe its context before doing further side effects.
138
+
120
139
  ### Liveness-safe job execution
121
140
 
122
141
  `TaskRunner` records heartbeat telemetry, but recovery keys on a worker
@@ -14,8 +14,14 @@
14
14
  * opt-in allowlist of methods that may be invoked by the runner. The runner's
15
15
  * dispatch is already bounded to existing prototype methods (no eval / dynamic
16
16
  * import), but a class can further restrict which of its methods are reachable
17
- * from a persisted job row. This same marker is intended to be consumed by the
18
- * agents package, which dispatches methods through an equivalent path.
17
+ * from a persisted job row.
18
+ *
19
+ * All three live in and are enforced by this package (`@happyvertical/smrt-jobs`).
20
+ * Other packages apply the marker (`smrt-reports`, `smrt-support`,
21
+ * `smrt-fields`), but the only thing that reads it is `TaskRunner`'s dispatch in
22
+ * `runner.ts`. `@happyvertical/smrt-agents` neither imports nor honours it, and
23
+ * does not depend on this package. Marking a method does **not** make it
24
+ * background-eligible anywhere outside the jobs runner.
19
25
  */
20
26
  /**
21
27
  * Hard ceiling on retry attempts a caller may request via `.retries(n)` /
@@ -60,9 +66,9 @@ export declare function assertWithinTenantCreationCap(tenantId: string | null |
60
66
  */
61
67
  export interface BackgroundEligibleClass {
62
68
  /**
63
- * Method names that may be invoked by the job/agent runner. When present
64
- * (even if empty), it is treated as an exhaustive allowlist. When absent,
65
- * the runner falls back to its default behaviour (any existing method).
69
+ * Method names that may be invoked by `TaskRunner`. When present (even if
70
+ * empty), it is treated as an exhaustive allowlist. When absent, the runner
71
+ * falls back to its default behaviour (any existing method).
66
72
  */
67
73
  backgroundEligibleMethods?: ReadonlyArray<string> | ReadonlySet<string>;
68
74
  }
@@ -73,8 +79,9 @@ export interface BackgroundEligibleClass {
73
79
  * Once any method is marked, the runner refuses to dispatch a job whose
74
80
  * `method` is not in the set — turning the dispatch surface from "any prototype
75
81
  * method" into an explicit contract. Use this when applying the
76
- * {@link backgroundEligible} decorator is inconvenient (or in non-decorator
77
- * code, including the agents package).
82
+ * {@link backgroundEligible} decorator is inconvenient. Non-decorator code can
83
+ * skip the helper entirely and declare the static array directly, as
84
+ * `smrt-fields` does in `usage-learning.ts`.
78
85
  *
79
86
  * @param ctor - The class constructor to annotate.
80
87
  * @param methods - Method names to allow.
@@ -83,11 +90,16 @@ export declare function markBackgroundEligible(ctor: object, ...methods: string[
83
90
  /**
84
91
  * Decorator: mark a method as background-eligible.
85
92
  *
93
+ * This is **restrictive, not enabling**. Without it, `TaskRunner` will dispatch
94
+ * any existing prototype method; the first `@backgroundEligible()` on a class
95
+ * closes that surface and turns the set into an exhaustive allowlist, so every
96
+ * *other* method on that class stops being reachable from a persisted job row.
97
+ * Adding it to one method of an existing class is therefore a behaviour change
98
+ * for its siblings — mark all the methods you dispatch, or none of them.
99
+ *
86
100
  * This is a legacy (`experimentalDecorators`) method decorator — the mode the
87
101
  * SMRT monorepo compiles with. Applying it (one or more times) builds up the
88
- * static `backgroundEligibleMethods` allowlist on the owning class. Once any
89
- * method is marked, the runner will refuse to dispatch a job whose `method` is
90
- * not in the set.
102
+ * static `backgroundEligibleMethods` allowlist on the owning class.
91
103
  *
92
104
  * @example
93
105
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"background-policy.d.ts","sourceRoot":"","sources":["../src/background-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,QAAQ,EAAE,MAAM;aAChB,GAAG,EAAE,MAAM;aACX,OAAO,EAAE,MAAM;gBAFf,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM;CAQlC;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,IAAI,CAKN;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;CACzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,GAAG,OAAO,EAAE,MAAM,EAAE,GACnB,IAAI,CASN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,KAE9B,QAAQ,MAAM,EACd,aAAa,MAAM,GAAG,MAAM,EAC5B,aAAa,kBAAkB,KAC9B,kBAAkB,GAAG,SAAS,CAOlC;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,OAAO,GACZ,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAK5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAIT"}
1
+ {"version":3,"file":"background-policy.d.ts","sourceRoot":"","sources":["../src/background-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,QAAQ,EAAE,MAAM;aAChB,GAAG,EAAE,MAAM;aACX,OAAO,EAAE,MAAM;gBAFf,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM;CAQlC;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,IAAI,CAKN;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;CACzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,GAAG,OAAO,EAAE,MAAM,EAAE,GACnB,IAAI,CASN;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,KAE9B,QAAQ,MAAM,EACd,aAAa,MAAM,GAAG,MAAM,EAC5B,aAAa,kBAAkB,KAC9B,kBAAkB,GAAG,SAAS,CAOlC;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,OAAO,GACZ,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAK5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAIT"}