@happyvertical/smrt-jobs 0.51.36 → 0.51.38
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/README.md +115 -0
- package/dist/chunks/{runner-DsprwGLU.js → runner-B4fIhQay.js} +161 -15
- package/dist/chunks/runner-B4fIhQay.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/logger-extension.d.ts +5 -0
- package/dist/logger-extension.d.ts.map +1 -1
- package/dist/manifest.json +25 -1
- package/dist/mcp-task.d.ts +42 -0
- package/dist/mcp-task.d.ts.map +1 -1
- package/dist/runner.d.ts +6 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +1 -1
- package/dist/smrt-job.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/package.json +7 -7
- package/dist/chunks/runner-DsprwGLU.js.map +0 -1
package/README.md
CHANGED
|
@@ -229,3 +229,118 @@ the same queue without duplicate-claiming a pending row.
|
|
|
229
229
|
- `@happyvertical/logger` -- structured logging
|
|
230
230
|
- `@happyvertical/utils` -- ID generation utilities
|
|
231
231
|
- Peer (optional): `@happyvertical/smrt-svelte`, `svelte`
|
|
232
|
+
|
|
233
|
+
### Restartable MCP input
|
|
234
|
+
|
|
235
|
+
For a workflow that must survive process restart, create the task with
|
|
236
|
+
`continuation: { recordId, revision, inputKey }`. These are immutable references
|
|
237
|
+
to an existing application review/action record, not approval or authority.
|
|
238
|
+
Create the store with both `ownerId` and `tenantId`; task creation must match
|
|
239
|
+
that active tenant. The application adapter must derive these values from its
|
|
240
|
+
verified principal, never tool arguments.
|
|
241
|
+
|
|
242
|
+
Configure `TaskRunner.authorizeMcpTask` to resolve live grants and validate the
|
|
243
|
+
bound actor/tenant against the owning record. It is mandatory for continuation tasks and for every remote app-created task,
|
|
244
|
+
and fails closed if absent or unavailable. Provider exceptions become the fixed
|
|
245
|
+
`MCP task authorization denied` error before task persistence; internal provider
|
|
246
|
+
details are never exposed through task status. It runs before each invocation;
|
|
247
|
+
`context.task.assertAuthorized()` repeats it and checks worker ownership before
|
|
248
|
+
apply. A permission snapshot must not be persisted in the job.
|
|
249
|
+
|
|
250
|
+
`context.task.requestContinuation(binding, inputDescriptor)` stores one input
|
|
251
|
+
round in the existing job row and suspends execution, releasing the worker.
|
|
252
|
+
Waiting rows are not claimable until `updateTask()` atomically stores the first
|
|
253
|
+
complete answer. A new worker starts the method from its beginning and retrieves
|
|
254
|
+
the saved answer. Calls before this seam must be read-only or use the owning
|
|
255
|
+
`runOnce`/action idempotency contract. The binding cannot change between rounds;
|
|
256
|
+
use a new owning action/task for another review revision. JSON input is bounded
|
|
257
|
+
to 64 KiB. Unknown response keys are ignored, incomplete responses do not wake
|
|
258
|
+
the job, and repeated answers cannot overwrite the first accepted response.
|
|
259
|
+
Once an answer is accepted, status is `working` while queued or resumed; the
|
|
260
|
+
immutable response remains available for replay without asking for input again.
|
|
261
|
+
|
|
262
|
+
`getContinuation(taskId)` exposes the waiting descriptor only through the same
|
|
263
|
+
actor/tenant-scoped store. Use it in an explicitly declared authorized workflow
|
|
264
|
+
tool or application form. It adds no fields to MCP `tasks/get`. The stateless
|
|
265
|
+
HTTP adapter retains no callback; absent elicitation, offer the application's
|
|
266
|
+
review URL/form. OpenAI MRTR is an optional protocol adapter responsibility and
|
|
267
|
+
is not implemented by this job API. The legacy `requestInput()` remains a
|
|
268
|
+
running-handler wait bounded by the job timeout and is not restartable.
|
|
269
|
+
|
|
270
|
+
Continuation tasks and remote app-created tasks do not automatically retry errors or unknown external outcomes.
|
|
271
|
+
Reconcile those through the owning action state's durable reservation/evidence
|
|
272
|
+
API; never blindly resubmit. Cancellation guards subsequent cooperative work
|
|
273
|
+
and terminal writes, but cannot retract an external side effect already sent.
|
|
274
|
+
The owning apply transaction must revalidate immutable review/revision and
|
|
275
|
+
idempotency. A host answer never creates final human approval; Iolaus continues
|
|
276
|
+
to use its dedicated human review page.
|
|
277
|
+
|
|
278
|
+
Persistence checks: `pnpm --filter @happyvertical/smrt-jobs test` and
|
|
279
|
+
`pnpm --filter @happyvertical/smrt-jobs test:postgres`. The continuation suite
|
|
280
|
+
also runs against a managed PostgreSQL database with:
|
|
281
|
+
|
|
282
|
+
```sh
|
|
283
|
+
node scripts/run-with-ci-postgres.mjs -- pnpm --filter @happyvertical/smrt-jobs test src/__tests__/mcp-continuation.optional.test.ts
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Supply `CI_POSTGRES_BASE_URL` for a disposable test server; the wrapper creates
|
|
287
|
+
and drops a synthetic database. Do not point destructive fixtures at user data.
|
|
288
|
+
|
|
289
|
+
Generated stdio servers retain their existing trusted-local-process assumption:
|
|
290
|
+
`SMRT_MCP_TENANT_ID` is a deployment scope and existing task owner namespace,
|
|
291
|
+
not an authenticated human identity. The generated store now also passes the
|
|
292
|
+
same active tenant used by task creation. Multi-user remote workflows require
|
|
293
|
+
the verified application principal path and a live runner authorizer.
|
|
294
|
+
|
|
295
|
+
The app integration exports `continueMcpWorkflow()` and
|
|
296
|
+
`createMcpContinuationTool()` from `@happyvertical/smrt-app-mcp`.
|
|
297
|
+
The first composes this job seam with an application's `applyReviewed` callback;
|
|
298
|
+
that callback must invoke its owning immutable approval and idempotency API,
|
|
299
|
+
not a raw mutation. The second declares a read-only workflow tool for a host
|
|
300
|
+
without elicitation. Register that tool through the app server's existing
|
|
301
|
+
`workflowTools` option. Supply a `storeFor(verifiedPrincipal)` bound to the same
|
|
302
|
+
owner namespace and tenant used for task creation, and a live `authorize`
|
|
303
|
+
callback. It returns text and structured form/review URL data; no iframe,
|
|
304
|
+
subscription, or host-specific form implementation is required. Review URLs
|
|
305
|
+
must use HTTPS, or HTTP on loopback for a local deployment, without credentials.
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
Remote `createMcpAppServer` task stores always set `requireAuthorization: true`.
|
|
309
|
+
This server-owned requirement is persisted separately from user invocation
|
|
310
|
+
arguments; tool arguments cannot disable it. An ordinary remote task without
|
|
311
|
+
continuation metadata must therefore configure the same live worker callback.
|
|
312
|
+
The legacy input seam also checks current authority before waiting and after
|
|
313
|
+
input returns. Applications must call `context.task.assertAuthorized()` again
|
|
314
|
+
immediately before later side effects. Direct `McpTaskStore` callers default to
|
|
315
|
+
trusted-local compatibility; set `requireAuthorization: true` for any remote
|
|
316
|
+
or multi-user application. This default does not confer human identity.
|
|
317
|
+
|
|
318
|
+
Shutdown includes any in-flight claim in the existing bounded drain and keeps
|
|
319
|
+
the worker lease until that drain finishes. A claim returning after shutdown
|
|
320
|
+
started is released without dispatch or consuming an attempt, even if the
|
|
321
|
+
shutdown deadline has elapsed; the next incarnation may claim it normally.
|
|
322
|
+
The same runner cannot restart while an earlier poll or handler is still draining.
|
|
323
|
+
|
|
324
|
+
### Durable continuation ready-index rollout
|
|
325
|
+
|
|
326
|
+
`_smrt_jobs_ready_idx` is an additive declared nonunique index on `(status,
|
|
327
|
+
run_at)`, restricted to `status = 'pending' AND (task_input_requests IS NULL
|
|
328
|
+
OR task_input_responses IS NOT NULL)`. Both `listReady()` and `claimReady()`
|
|
329
|
+
use this exact eligibility predicate. Suspended already-due jobs remain pending,
|
|
330
|
+
but SQLite/PostgreSQL exclude them from this index until an answer wakes them.
|
|
331
|
+
The original `_smrt_jobs_status_run_at_idx` remains for other status/time reads.
|
|
332
|
+
|
|
333
|
+
Deploy through the owning schema migration path: run `smrt db:migrate` before
|
|
334
|
+
running the updated workers; for continuously written PostgreSQL queues use
|
|
335
|
+
`smrt db:migrate --postgres-safe` so the index builds concurrently. No runtime
|
|
336
|
+
task/runner API creates the index. The old schema remains functionally correct
|
|
337
|
+
until migration, but polling can scan the suspended backlog. This changes no
|
|
338
|
+
column or task status. DuckDB/JSON use the existing DDL strategy's full-index
|
|
339
|
+
fallback and do **not** gain suspended-backlog exclusion; the readiness filter
|
|
340
|
+
still preserves correctness on those engines.
|
|
341
|
+
|
|
342
|
+
Task creation snapshots only validated `recordId`, `revision`, and `inputKey`
|
|
343
|
+
values before any awaited enqueue work. Extra caller fields are not persisted,
|
|
344
|
+
and later mutation of the caller's binding cannot change the durable review
|
|
345
|
+
reference. Live authorization still checks the saved reference at execution
|
|
346
|
+
and resume; the snapshot never grants approval.
|