@smthrs/control 0.0.0-stage → 1.0.0-rc.4
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/CHANGELOG.md +194 -0
- package/LICENSE +21 -0
- package/README.md +168 -2
- package/dist/cjs/ApprovalAuthority.d.ts +73 -0
- package/dist/cjs/ApprovalAuthority.d.ts.map +1 -0
- package/dist/cjs/ApprovalAuthority.js +62 -0
- package/dist/cjs/ApprovalAuthority.js.map +7 -0
- package/dist/cjs/Cancellation.d.ts +107 -0
- package/dist/cjs/Cancellation.d.ts.map +1 -0
- package/dist/cjs/Cancellation.js +72 -0
- package/dist/cjs/Cancellation.js.map +7 -0
- package/dist/cjs/Channels.d.ts +170 -0
- package/dist/cjs/Channels.d.ts.map +1 -0
- package/dist/cjs/Channels.js +278 -0
- package/dist/cjs/Channels.js.map +7 -0
- package/dist/cjs/Control.d.ts +202 -0
- package/dist/cjs/Control.d.ts.map +1 -0
- package/dist/cjs/Control.js +47 -0
- package/dist/cjs/Control.js.map +7 -0
- package/dist/cjs/ControlClient.d.ts +52 -0
- package/dist/cjs/ControlClient.d.ts.map +1 -0
- package/dist/cjs/ControlClient.js +191 -0
- package/dist/cjs/ControlClient.js.map +7 -0
- package/dist/cjs/ControlError.d.ts +318 -0
- package/dist/cjs/ControlError.d.ts.map +1 -0
- package/dist/cjs/ControlError.js +249 -0
- package/dist/cjs/ControlError.js.map +7 -0
- package/dist/cjs/ControlExecutor.d.ts +372 -0
- package/dist/cjs/ControlExecutor.d.ts.map +1 -0
- package/dist/cjs/ControlExecutor.js +123 -0
- package/dist/cjs/ControlExecutor.js.map +7 -0
- package/dist/cjs/ControlFacts.d.ts +454 -0
- package/dist/cjs/ControlFacts.d.ts.map +1 -0
- package/dist/cjs/ControlFacts.js +261 -0
- package/dist/cjs/ControlFacts.js.map +7 -0
- package/dist/cjs/ControlLive.d.ts +23 -0
- package/dist/cjs/ControlLive.d.ts.map +1 -0
- package/dist/cjs/ControlLive.js +1293 -0
- package/dist/cjs/ControlLive.js.map +7 -0
- package/dist/cjs/ControlRpcs.d.ts +1204 -0
- package/dist/cjs/ControlRpcs.d.ts.map +1 -0
- package/dist/cjs/ControlRpcs.js +247 -0
- package/dist/cjs/ControlRpcs.js.map +7 -0
- package/dist/cjs/ControlRuntime.d.ts +635 -0
- package/dist/cjs/ControlRuntime.d.ts.map +1 -0
- package/dist/cjs/ControlRuntime.js +744 -0
- package/dist/cjs/ControlRuntime.js.map +7 -0
- package/dist/cjs/ControlSchema.d.ts +2642 -0
- package/dist/cjs/ControlSchema.d.ts.map +1 -0
- package/dist/cjs/ControlSchema.js +634 -0
- package/dist/cjs/ControlSchema.js.map +7 -0
- package/dist/cjs/ControlServer.d.ts +51 -0
- package/dist/cjs/ControlServer.d.ts.map +1 -0
- package/dist/cjs/ControlServer.js +121 -0
- package/dist/cjs/ControlServer.js.map +7 -0
- package/dist/cjs/Credential.d.ts +136 -0
- package/dist/cjs/Credential.d.ts.map +1 -0
- package/dist/cjs/Credential.js +168 -0
- package/dist/cjs/Credential.js.map +7 -0
- package/dist/cjs/CredentialCipher.d.ts +90 -0
- package/dist/cjs/CredentialCipher.d.ts.map +1 -0
- package/dist/cjs/CredentialCipher.js +45 -0
- package/dist/cjs/CredentialCipher.js.map +7 -0
- package/dist/cjs/CredentialStore.d.ts +97 -0
- package/dist/cjs/CredentialStore.d.ts.map +1 -0
- package/dist/cjs/CredentialStore.js +81 -0
- package/dist/cjs/CredentialStore.js.map +7 -0
- package/dist/cjs/DispatchReader.d.ts +112 -0
- package/dist/cjs/DispatchReader.d.ts.map +1 -0
- package/dist/cjs/DispatchReader.js +45 -0
- package/dist/cjs/DispatchReader.js.map +7 -0
- package/dist/cjs/Health.d.ts +333 -0
- package/dist/cjs/Health.d.ts.map +1 -0
- package/dist/cjs/Health.js +311 -0
- package/dist/cjs/Health.js.map +7 -0
- package/dist/cjs/JevSessionChecker.d.ts +57 -0
- package/dist/cjs/JevSessionChecker.d.ts.map +1 -0
- package/dist/cjs/JevSessionChecker.js +113 -0
- package/dist/cjs/JevSessionChecker.js.map +7 -0
- package/dist/cjs/Lineage.d.ts +131 -0
- package/dist/cjs/Lineage.d.ts.map +1 -0
- package/dist/cjs/Lineage.js +81 -0
- package/dist/cjs/Lineage.js.map +7 -0
- package/dist/cjs/Migrations.d.ts +34 -0
- package/dist/cjs/Migrations.d.ts.map +1 -0
- package/dist/cjs/Migrations.js +60 -0
- package/dist/cjs/Migrations.js.map +7 -0
- package/dist/cjs/Monitor.d.ts +282 -0
- package/dist/cjs/Monitor.d.ts.map +1 -0
- package/dist/cjs/Monitor.js +283 -0
- package/dist/cjs/Monitor.js.map +7 -0
- package/dist/cjs/ScopedToken.d.ts +193 -0
- package/dist/cjs/ScopedToken.d.ts.map +1 -0
- package/dist/cjs/ScopedToken.js +135 -0
- package/dist/cjs/ScopedToken.js.map +7 -0
- package/dist/cjs/SqlControlRuntime.d.ts +161 -0
- package/dist/cjs/SqlControlRuntime.d.ts.map +1 -0
- package/dist/cjs/SqlControlRuntime.js +1522 -0
- package/dist/cjs/SqlControlRuntime.js.map +7 -0
- package/dist/cjs/SqlCredentialStore.d.ts +43 -0
- package/dist/cjs/SqlCredentialStore.d.ts.map +1 -0
- package/dist/cjs/SqlCredentialStore.js +113 -0
- package/dist/cjs/SqlCredentialStore.js.map +7 -0
- package/dist/cjs/Steering.d.ts +69 -0
- package/dist/cjs/Steering.d.ts.map +1 -0
- package/dist/cjs/Steering.js +49 -0
- package/dist/cjs/Steering.js.map +7 -0
- package/dist/cjs/SystemFlows.d.ts +223 -0
- package/dist/cjs/SystemFlows.d.ts.map +1 -0
- package/dist/cjs/SystemFlows.js +195 -0
- package/dist/cjs/SystemFlows.js.map +7 -0
- package/dist/cjs/WebCryptoCipher.d.ts +49 -0
- package/dist/cjs/WebCryptoCipher.d.ts.map +1 -0
- package/dist/cjs/WebCryptoCipher.js +129 -0
- package/dist/cjs/WebCryptoCipher.js.map +7 -0
- package/dist/cjs/WebhookChannel.d.ts +113 -0
- package/dist/cjs/WebhookChannel.d.ts.map +1 -0
- package/dist/cjs/WebhookChannel.js +98 -0
- package/dist/cjs/WebhookChannel.js.map +7 -0
- package/dist/cjs/index.d.ts +160 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +91 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/MutationBoundary.d.ts +27 -0
- package/dist/cjs/internal/MutationBoundary.d.ts.map +1 -0
- package/dist/cjs/internal/MutationBoundary.js +50 -0
- package/dist/cjs/internal/MutationBoundary.js.map +7 -0
- package/dist/cjs/internal/activeFibers.d.ts +12 -0
- package/dist/cjs/internal/activeFibers.d.ts.map +1 -0
- package/dist/cjs/internal/activeFibers.js +30 -0
- package/dist/cjs/internal/activeFibers.js.map +7 -0
- package/dist/cjs/internal/issues.d.ts +28 -0
- package/dist/cjs/internal/issues.d.ts.map +1 -0
- package/dist/cjs/internal/issues.js +34 -0
- package/dist/cjs/internal/issues.js.map +7 -0
- package/dist/cjs/internal/planning.d.ts +347 -0
- package/dist/cjs/internal/planning.d.ts.map +1 -0
- package/dist/cjs/internal/planning.js +137 -0
- package/dist/cjs/internal/planning.js.map +7 -0
- package/dist/cjs/internal/sqlSchemaErrors.d.ts +18 -0
- package/dist/cjs/internal/sqlSchemaErrors.d.ts.map +1 -0
- package/dist/cjs/internal/sqlSchemaErrors.js +40 -0
- package/dist/cjs/internal/sqlSchemaErrors.js.map +7 -0
- package/dist/cjs/migrations/0001_control_tables.d.ts +18 -0
- package/dist/cjs/migrations/0001_control_tables.d.ts.map +1 -0
- package/dist/cjs/migrations/0001_control_tables.js +115 -0
- package/dist/cjs/migrations/0001_control_tables.js.map +7 -0
- package/dist/cjs/migrations/0002_run_keys.d.ts +15 -0
- package/dist/cjs/migrations/0002_run_keys.d.ts.map +1 -0
- package/dist/cjs/migrations/0002_run_keys.js +44 -0
- package/dist/cjs/migrations/0002_run_keys.js.map +7 -0
- package/dist/cjs/migrations/0003_signal_commands.d.ts +13 -0
- package/dist/cjs/migrations/0003_signal_commands.d.ts.map +1 -0
- package/dist/cjs/migrations/0003_signal_commands.js +50 -0
- package/dist/cjs/migrations/0003_signal_commands.js.map +7 -0
- package/dist/cjs/migrations/0004_approval_decisions.d.ts +13 -0
- package/dist/cjs/migrations/0004_approval_decisions.d.ts.map +1 -0
- package/dist/cjs/migrations/0004_approval_decisions.js +45 -0
- package/dist/cjs/migrations/0004_approval_decisions.js.map +7 -0
- package/dist/cjs/migrations/0005_signal_principals.d.ts +18 -0
- package/dist/cjs/migrations/0005_signal_principals.d.ts.map +1 -0
- package/dist/cjs/migrations/0005_signal_principals.js +45 -0
- package/dist/cjs/migrations/0005_signal_principals.js.map +7 -0
- package/dist/cjs/migrations/0006_run_principals.d.ts +18 -0
- package/dist/cjs/migrations/0006_run_principals.d.ts.map +1 -0
- package/dist/cjs/migrations/0006_run_principals.js +49 -0
- package/dist/cjs/migrations/0006_run_principals.js.map +7 -0
- package/dist/cjs/migrations/0007_resume_consent.d.ts +18 -0
- package/dist/cjs/migrations/0007_resume_consent.d.ts.map +1 -0
- package/dist/cjs/migrations/0007_resume_consent.js +46 -0
- package/dist/cjs/migrations/0007_resume_consent.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestControl.d.ts +19 -0
- package/dist/cjs/test/TestControl.d.ts.map +1 -0
- package/dist/cjs/test/TestControl.js +62 -0
- package/dist/cjs/test/TestControl.js.map +7 -0
- package/dist/esm/ApprovalAuthority.d.ts +73 -0
- package/dist/esm/ApprovalAuthority.d.ts.map +1 -0
- package/dist/esm/ApprovalAuthority.js +72 -0
- package/dist/esm/ApprovalAuthority.js.map +1 -0
- package/dist/esm/Cancellation.d.ts +107 -0
- package/dist/esm/Cancellation.d.ts.map +1 -0
- package/dist/esm/Cancellation.js +116 -0
- package/dist/esm/Cancellation.js.map +1 -0
- package/dist/esm/Channels.d.ts +170 -0
- package/dist/esm/Channels.d.ts.map +1 -0
- package/dist/esm/Channels.js +312 -0
- package/dist/esm/Channels.js.map +1 -0
- package/dist/esm/Control.d.ts +202 -0
- package/dist/esm/Control.d.ts.map +1 -0
- package/dist/esm/Control.js +42 -0
- package/dist/esm/Control.js.map +1 -0
- package/dist/esm/ControlClient.d.ts +52 -0
- package/dist/esm/ControlClient.d.ts.map +1 -0
- package/dist/esm/ControlClient.js +217 -0
- package/dist/esm/ControlClient.js.map +1 -0
- package/dist/esm/ControlError.d.ts +318 -0
- package/dist/esm/ControlError.d.ts.map +1 -0
- package/dist/esm/ControlError.js +359 -0
- package/dist/esm/ControlError.js.map +1 -0
- package/dist/esm/ControlExecutor.d.ts +372 -0
- package/dist/esm/ControlExecutor.d.ts.map +1 -0
- package/dist/esm/ControlExecutor.js +212 -0
- package/dist/esm/ControlExecutor.js.map +1 -0
- package/dist/esm/ControlFacts.d.ts +454 -0
- package/dist/esm/ControlFacts.d.ts.map +1 -0
- package/dist/esm/ControlFacts.js +324 -0
- package/dist/esm/ControlFacts.js.map +1 -0
- package/dist/esm/ControlLive.d.ts +23 -0
- package/dist/esm/ControlLive.d.ts.map +1 -0
- package/dist/esm/ControlLive.js +1589 -0
- package/dist/esm/ControlLive.js.map +1 -0
- package/dist/esm/ControlRpcs.d.ts +1204 -0
- package/dist/esm/ControlRpcs.d.ts.map +1 -0
- package/dist/esm/ControlRpcs.js +299 -0
- package/dist/esm/ControlRpcs.js.map +1 -0
- package/dist/esm/ControlRuntime.d.ts +635 -0
- package/dist/esm/ControlRuntime.d.ts.map +1 -0
- package/dist/esm/ControlRuntime.js +808 -0
- package/dist/esm/ControlRuntime.js.map +1 -0
- package/dist/esm/ControlSchema.d.ts +2642 -0
- package/dist/esm/ControlSchema.d.ts.map +1 -0
- package/dist/esm/ControlSchema.js +1030 -0
- package/dist/esm/ControlSchema.js.map +1 -0
- package/dist/esm/ControlServer.d.ts +51 -0
- package/dist/esm/ControlServer.d.ts.map +1 -0
- package/dist/esm/ControlServer.js +145 -0
- package/dist/esm/ControlServer.js.map +1 -0
- package/dist/esm/Credential.d.ts +136 -0
- package/dist/esm/Credential.d.ts.map +1 -0
- package/dist/esm/Credential.js +190 -0
- package/dist/esm/Credential.js.map +1 -0
- package/dist/esm/CredentialCipher.d.ts +90 -0
- package/dist/esm/CredentialCipher.d.ts.map +1 -0
- package/dist/esm/CredentialCipher.js +56 -0
- package/dist/esm/CredentialCipher.js.map +1 -0
- package/dist/esm/CredentialStore.d.ts +97 -0
- package/dist/esm/CredentialStore.d.ts.map +1 -0
- package/dist/esm/CredentialStore.js +101 -0
- package/dist/esm/CredentialStore.js.map +1 -0
- package/dist/esm/DispatchReader.d.ts +112 -0
- package/dist/esm/DispatchReader.d.ts.map +1 -0
- package/dist/esm/DispatchReader.js +76 -0
- package/dist/esm/DispatchReader.js.map +1 -0
- package/dist/esm/Health.d.ts +333 -0
- package/dist/esm/Health.d.ts.map +1 -0
- package/dist/esm/Health.js +400 -0
- package/dist/esm/Health.js.map +1 -0
- package/dist/esm/JevSessionChecker.d.ts +57 -0
- package/dist/esm/JevSessionChecker.d.ts.map +1 -0
- package/dist/esm/JevSessionChecker.js +108 -0
- package/dist/esm/JevSessionChecker.js.map +1 -0
- package/dist/esm/Lineage.d.ts +131 -0
- package/dist/esm/Lineage.d.ts.map +1 -0
- package/dist/esm/Lineage.js +174 -0
- package/dist/esm/Lineage.js.map +1 -0
- package/dist/esm/Migrations.d.ts +34 -0
- package/dist/esm/Migrations.d.ts.map +1 -0
- package/dist/esm/Migrations.js +53 -0
- package/dist/esm/Migrations.js.map +1 -0
- package/dist/esm/Monitor.d.ts +282 -0
- package/dist/esm/Monitor.d.ts.map +1 -0
- package/dist/esm/Monitor.js +415 -0
- package/dist/esm/Monitor.js.map +1 -0
- package/dist/esm/ScopedToken.d.ts +193 -0
- package/dist/esm/ScopedToken.d.ts.map +1 -0
- package/dist/esm/ScopedToken.js +224 -0
- package/dist/esm/ScopedToken.js.map +1 -0
- package/dist/esm/SqlControlRuntime.d.ts +161 -0
- package/dist/esm/SqlControlRuntime.d.ts.map +1 -0
- package/dist/esm/SqlControlRuntime.js +1756 -0
- package/dist/esm/SqlControlRuntime.js.map +1 -0
- package/dist/esm/SqlCredentialStore.d.ts +43 -0
- package/dist/esm/SqlCredentialStore.d.ts.map +1 -0
- package/dist/esm/SqlCredentialStore.js +97 -0
- package/dist/esm/SqlCredentialStore.js.map +1 -0
- package/dist/esm/Steering.d.ts +69 -0
- package/dist/esm/Steering.d.ts.map +1 -0
- package/dist/esm/Steering.js +89 -0
- package/dist/esm/Steering.js.map +1 -0
- package/dist/esm/SystemFlows.d.ts +223 -0
- package/dist/esm/SystemFlows.d.ts.map +1 -0
- package/dist/esm/SystemFlows.js +198 -0
- package/dist/esm/SystemFlows.js.map +1 -0
- package/dist/esm/WebCryptoCipher.d.ts +49 -0
- package/dist/esm/WebCryptoCipher.d.ts.map +1 -0
- package/dist/esm/WebCryptoCipher.js +123 -0
- package/dist/esm/WebCryptoCipher.js.map +1 -0
- package/dist/esm/WebhookChannel.d.ts +113 -0
- package/dist/esm/WebhookChannel.d.ts.map +1 -0
- package/dist/esm/WebhookChannel.js +109 -0
- package/dist/esm/WebhookChannel.js.map +1 -0
- package/dist/esm/index.d.ts +160 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +160 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/MutationBoundary.d.ts +27 -0
- package/dist/esm/internal/MutationBoundary.d.ts.map +1 -0
- package/dist/esm/internal/MutationBoundary.js +40 -0
- package/dist/esm/internal/MutationBoundary.js.map +1 -0
- package/dist/esm/internal/activeFibers.d.ts +12 -0
- package/dist/esm/internal/activeFibers.d.ts.map +1 -0
- package/dist/esm/internal/activeFibers.js +17 -0
- package/dist/esm/internal/activeFibers.js.map +1 -0
- package/dist/esm/internal/issues.d.ts +28 -0
- package/dist/esm/internal/issues.d.ts.map +1 -0
- package/dist/esm/internal/issues.js +35 -0
- package/dist/esm/internal/issues.js.map +1 -0
- package/dist/esm/internal/planning.d.ts +347 -0
- package/dist/esm/internal/planning.d.ts.map +1 -0
- package/dist/esm/internal/planning.js +199 -0
- package/dist/esm/internal/planning.js.map +1 -0
- package/dist/esm/internal/sqlSchemaErrors.d.ts +18 -0
- package/dist/esm/internal/sqlSchemaErrors.d.ts.map +1 -0
- package/dist/esm/internal/sqlSchemaErrors.js +38 -0
- package/dist/esm/internal/sqlSchemaErrors.js.map +1 -0
- package/dist/esm/migrations/0001_control_tables.d.ts +18 -0
- package/dist/esm/migrations/0001_control_tables.d.ts.map +1 -0
- package/dist/esm/migrations/0001_control_tables.js +96 -0
- package/dist/esm/migrations/0001_control_tables.js.map +1 -0
- package/dist/esm/migrations/0002_run_keys.d.ts +15 -0
- package/dist/esm/migrations/0002_run_keys.d.ts.map +1 -0
- package/dist/esm/migrations/0002_run_keys.js +22 -0
- package/dist/esm/migrations/0002_run_keys.js.map +1 -0
- package/dist/esm/migrations/0003_signal_commands.d.ts +13 -0
- package/dist/esm/migrations/0003_signal_commands.d.ts.map +1 -0
- package/dist/esm/migrations/0003_signal_commands.js +26 -0
- package/dist/esm/migrations/0003_signal_commands.js.map +1 -0
- package/dist/esm/migrations/0004_approval_decisions.d.ts +13 -0
- package/dist/esm/migrations/0004_approval_decisions.d.ts.map +1 -0
- package/dist/esm/migrations/0004_approval_decisions.js +25 -0
- package/dist/esm/migrations/0004_approval_decisions.js.map +1 -0
- package/dist/esm/migrations/0005_signal_principals.d.ts +18 -0
- package/dist/esm/migrations/0005_signal_principals.d.ts.map +1 -0
- package/dist/esm/migrations/0005_signal_principals.js +27 -0
- package/dist/esm/migrations/0005_signal_principals.js.map +1 -0
- package/dist/esm/migrations/0006_run_principals.d.ts +18 -0
- package/dist/esm/migrations/0006_run_principals.d.ts.map +1 -0
- package/dist/esm/migrations/0006_run_principals.js +30 -0
- package/dist/esm/migrations/0006_run_principals.js.map +1 -0
- package/dist/esm/migrations/0007_resume_consent.d.ts +18 -0
- package/dist/esm/migrations/0007_resume_consent.d.ts.map +1 -0
- package/dist/esm/migrations/0007_resume_consent.js +27 -0
- package/dist/esm/migrations/0007_resume_consent.js.map +1 -0
- package/dist/esm/test/TestControl.d.ts +19 -0
- package/dist/esm/test/TestControl.d.ts.map +1 -0
- package/dist/esm/test/TestControl.js +30 -0
- package/dist/esm/test/TestControl.js.map +1 -0
- package/docs/README.md +189 -0
- package/docs/api.md +982 -0
- package/docs/concepts/authority.md +109 -0
- package/docs/concepts/cancellation.md +129 -0
- package/docs/concepts/lineage.md +132 -0
- package/docs/concepts/ownership.md +139 -0
- package/docs/concepts/projections.md +203 -0
- package/docs/concepts/receipts.md +128 -0
- package/docs/guides/approvals.md +284 -0
- package/docs/guides/cancel-and-resume.md +162 -0
- package/docs/guides/durable-storage.md +147 -0
- package/docs/guides/implement-an-executor.md +173 -0
- package/docs/guides/ingest-a-webhook.md +177 -0
- package/docs/guides/list-runs.md +160 -0
- package/docs/guides/monitor-runs.md +176 -0
- package/docs/guides/observe-health.md +147 -0
- package/docs/guides/postgres-tests.md +7 -0
- package/docs/guides/serve-over-rpc.md +220 -0
- package/docs/guides/signal-a-run.md +53 -0
- package/docs/guides/steer-a-run.md +138 -0
- package/docs/guides/store-credentials.md +164 -0
- package/docs/guides/testing.md +139 -0
- package/docs/guides/watch-a-run.md +154 -0
- package/docs/installation.md +106 -0
- package/docs/quickstart.md +163 -0
- package/docs/troubleshooting.md +208 -0
- package/package.json +405 -3
- package/src/ApprovalAuthority.ts +114 -0
- package/src/Cancellation.ts +172 -0
- package/src/Channels.ts +493 -0
- package/src/Control.ts +337 -0
- package/src/ControlClient.ts +319 -0
- package/src/ControlError.ts +378 -0
- package/src/ControlExecutor.ts +490 -0
- package/src/ControlFacts.ts +383 -0
- package/src/ControlLive.ts +2127 -0
- package/src/ControlRpcs.ts +443 -0
- package/src/ControlRuntime.ts +1601 -0
- package/src/ControlSchema.ts +1380 -0
- package/src/ControlServer.ts +182 -0
- package/src/Credential.ts +310 -0
- package/src/CredentialCipher.ts +110 -0
- package/src/CredentialStore.ts +152 -0
- package/src/DispatchReader.ts +122 -0
- package/src/Health.ts +591 -0
- package/src/JevSessionChecker.ts +127 -0
- package/src/Lineage.ts +203 -0
- package/src/Migrations.ts +56 -0
- package/src/Monitor.ts +600 -0
- package/src/ScopedToken.ts +306 -0
- package/src/SqlControlRuntime.ts +2478 -0
- package/src/SqlCredentialStore.ts +148 -0
- package/src/Steering.ts +96 -0
- package/src/SystemFlows.ts +225 -0
- package/src/WebCryptoCipher.ts +169 -0
- package/src/WebhookChannel.ts +166 -0
- package/src/index.ts +188 -0
- package/src/internal/MutationBoundary.ts +46 -0
- package/src/internal/activeFibers.ts +22 -0
- package/src/internal/issues.ts +40 -0
- package/src/internal/planning.ts +262 -0
- package/src/internal/sqlSchemaErrors.ts +37 -0
- package/src/migrations/0001_control_tables.ts +99 -0
- package/src/migrations/0002_run_keys.ts +23 -0
- package/src/migrations/0003_signal_commands.ts +27 -0
- package/src/migrations/0004_approval_decisions.ts +25 -0
- package/src/migrations/0005_signal_principals.ts +27 -0
- package/src/migrations/0006_run_principals.ts +31 -0
- package/src/migrations/0007_resume_consent.ts +28 -0
- package/src/test/TestControl.ts +47 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Find runs and page through them"
|
|
3
|
+
description: "List the flows a host can plan and the runs it knows about: the run filters, the page bounds a listing enforces, the cursor contract, and the two refusals a listing answers instead of guessing."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`list` answers four questions under one verb, selected by the request's tag:
|
|
9
|
+
what can this host plan, what runs exist, which triggers are registered, and
|
|
10
|
+
what became of each trigger occurrence.
|
|
11
|
+
|
|
12
|
+
## List the flows a host can plan
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
const catalogued = yield * control.list({ _tag: "flows" })
|
|
16
|
+
const flows = catalogued._tag === "flows" ? catalogued.items : []
|
|
17
|
+
// [{ flowId: "ops/Deploy", description: "Deploys one build" }]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The items come from the [registry](/api/registry) when it has discovered
|
|
21
|
+
anything, and fall back to the runtime's own flow catalog when it has not.
|
|
22
|
+
`warnings` carries the registry's discovery diagnostics when a scan produced
|
|
23
|
+
any, so a flow that failed to load is visible rather than silently missing.
|
|
24
|
+
|
|
25
|
+
## List runs
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const listed = yield * control.list({ _tag: "runs", filters: { status: "parked" } })
|
|
29
|
+
const runs = listed._tag === "runs" ? listed.items : []
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Six filters are supported, and they combine:
|
|
33
|
+
|
|
34
|
+
| Filter | Selects |
|
|
35
|
+
| ------------- | ------------------------------------------------------------ |
|
|
36
|
+
| `runId` | Exactly one run, read directly rather than scanned. |
|
|
37
|
+
| `flowId` | Runs of one flow. |
|
|
38
|
+
| `status` | Runs in one of the seven `RunStatus` values. |
|
|
39
|
+
| `terminal` | `true`: completed, failed or cancelled; `false`: unfinished. |
|
|
40
|
+
| `parentRunId` | The runs one run spawned, forked, or handed off to. |
|
|
41
|
+
| `lineageId` | Every round of one trampoline lineage. |
|
|
42
|
+
|
|
43
|
+
Filtering on `runId` is one read. Other queries filter before decoding the
|
|
44
|
+
selected page. An executor that supplies observed status is post-filtered to
|
|
45
|
+
keep the returned status consistent: a `status` or `terminal` page walks the
|
|
46
|
+
source until it is full, so it can observe more runs than `limit`.
|
|
47
|
+
|
|
48
|
+
Use `order: "newest"` to order by creation time descending, with stable
|
|
49
|
+
sequence/id tie breakers. Omission preserves the historical order. A cursor
|
|
50
|
+
is bound to both its filters and order. For example, duration history asks:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const recent = yield * control.list({
|
|
54
|
+
_tag: "runs",
|
|
55
|
+
filters: { flowId: "ops/Deploy", terminal: true },
|
|
56
|
+
order: "newest",
|
|
57
|
+
limit: 20
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`filters.principalId` selects the runs a principal with that id launched
|
|
62
|
+
(`RunSummary.launchedBy`). It narrows a listing and is not a tenant boundary:
|
|
63
|
+
over RPC the server already restricts a principal that is not an operator to
|
|
64
|
+
its own runs, whatever filter it sends. See
|
|
65
|
+
[serve over RPC](./serve-over-rpc.md#who-reads-which-runs).
|
|
66
|
+
|
|
67
|
+
## List triggers and their fires
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const registered = yield * control.list({ _tag: "triggers", filters: { enabled: true } })
|
|
71
|
+
const triggers = registered._tag === "triggers" ? registered.items : []
|
|
72
|
+
// [{ triggerId: "nightly-lint", flowId: "lint", cron: "0 3 * * *", enabled: true, nextOccurrencesMs: [...], ... }]
|
|
73
|
+
|
|
74
|
+
const ledger = yield * control.list({ _tag: "fires", filters: { runId } })
|
|
75
|
+
const fires = ledger._tag === "fires" ? ledger.items : []
|
|
76
|
+
// [{ triggerId: "nightly-lint", occurrenceAtMs, outcome: "launched", runId }]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`triggers` filters on `triggerId`, `flowId`, and `enabled`; `fires` filters on
|
|
80
|
+
`triggerId`, `runId`, and `outcome`, and the ledger comes back newest first. Both
|
|
81
|
+
are read through the `DispatchReader` port the host composes over its trigger
|
|
82
|
+
store. A host that provides none refuses both variants with `InvalidInput` and
|
|
83
|
+
the issue `this host serves no trigger store`. It never answers an empty page
|
|
84
|
+
for them, because an empty page would claim the host has no triggers when it
|
|
85
|
+
cannot read its store at all.
|
|
86
|
+
|
|
87
|
+
## Page through the result
|
|
88
|
+
|
|
89
|
+
A listing is bounded, always:
|
|
90
|
+
|
|
91
|
+
| Bound | Value |
|
|
92
|
+
| ----------------- | ------------------------------------ |
|
|
93
|
+
| Default page size | `ControlSchema.defaultPageSize`, 100 |
|
|
94
|
+
| Maximum page size | `ControlSchema.maxPageSize`, 500 |
|
|
95
|
+
|
|
96
|
+
`nextCursor` is present exactly when more rows follow. Pass it back as
|
|
97
|
+
`cursor`:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import * as Effect from "effect/Effect"
|
|
101
|
+
|
|
102
|
+
const everyRun = Effect.gen(function*() {
|
|
103
|
+
const control = yield* Control
|
|
104
|
+
const items = []
|
|
105
|
+
let cursor: string | undefined
|
|
106
|
+
do {
|
|
107
|
+
const page = yield* control.list({
|
|
108
|
+
_tag: "runs",
|
|
109
|
+
limit: 200,
|
|
110
|
+
...(cursor === undefined ? {} : { cursor })
|
|
111
|
+
})
|
|
112
|
+
if (page._tag !== "runs") break
|
|
113
|
+
items.push(...page.items)
|
|
114
|
+
cursor = page.nextCursor
|
|
115
|
+
} while (cursor !== undefined)
|
|
116
|
+
return items
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Only a cursor this listing issued is accepted. A `limit` outside 1 to 500, and
|
|
121
|
+
an unparsable cursor, are both refused with `InvalidInput` rather than answered
|
|
122
|
+
with a plausible page:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
InvalidInput: limit: must be an integer between 1 and 500, received 0
|
|
126
|
+
InvalidInput: cursor: must be a cursor this listing returned, received "abc"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## What a summary carries
|
|
130
|
+
|
|
131
|
+
`RunSummary` is the projection every listing returns. Beyond `runId`,
|
|
132
|
+
`flowId`, `status`, `createdAt`, and `updatedAt`:
|
|
133
|
+
|
|
134
|
+
| Field | Present when |
|
|
135
|
+
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `planId`, `planDigest` | The run was launched from a plan on this plane. |
|
|
137
|
+
| `ownerId` | A process holds the row. |
|
|
138
|
+
| `parentRunId`, `lineageId`, `roundOrdinal`, `origin` | The run has an ancestor. See [run lineage](../concepts/lineage.md). |
|
|
139
|
+
| `waitingReason` | The engine parked the run and named a reason. |
|
|
140
|
+
| `parkedBy` | The park was written under a fence. |
|
|
141
|
+
| `pendingResume` | A resume has been recorded and no host has taken it up. |
|
|
142
|
+
| `steering` | The notification queue answered how many steers are pending. |
|
|
143
|
+
| `cancellation` | Somebody or something cancelled the run. See [cancellation attribution](../concepts/cancellation.md). |
|
|
144
|
+
|
|
145
|
+
`steering.pending` is read from the queue rather than from a column, because
|
|
146
|
+
pending is admitted minus promoted and the queue owns both halves. A queue that
|
|
147
|
+
cannot answer leaves the field absent, because "not known" is representable and
|
|
148
|
+
it is the truth.
|
|
149
|
+
|
|
150
|
+
Several of these fields are filled in only by the durable runtime, and only
|
|
151
|
+
when it shares a database with the engine. See
|
|
152
|
+
[Store control state in a database](./durable-storage.md).
|
|
153
|
+
|
|
154
|
+
## Where to go next
|
|
155
|
+
|
|
156
|
+
- [Watch a run's events](./watch-a-run.md): the same runs, as they change.
|
|
157
|
+
- [Run lineage](../concepts/lineage.md): what `parentRunId` and `lineageId`
|
|
158
|
+
select.
|
|
159
|
+
- [`smthrs runs list`](/cli/runs) and `smthrs runs show`: the operator
|
|
160
|
+
surface over this verb.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Monitor a run and heal it"
|
|
3
|
+
description: "Classify a run's health from durable evidence, beat over the control plane, and apply a bounded remedy: the seven healths, the two records a beat writes, and why autoHeal is empty by default."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 10
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A control plane answers "what is this run doing". `Monitor` answers the
|
|
9
|
+
question after it: is that all right, and if not, what now.
|
|
10
|
+
|
|
11
|
+
`classify` is pure, so the vocabulary an operator reads on a dashboard is the
|
|
12
|
+
one a heal loop branches on and the one a test can enumerate.
|
|
13
|
+
|
|
14
|
+
## Classify one observation
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import * as Monitor from "@smthrs/control/Monitor"
|
|
18
|
+
|
|
19
|
+
Monitor.classify({
|
|
20
|
+
summary,
|
|
21
|
+
events, // the run's journal, oldest first
|
|
22
|
+
beatsWithoutProgress: 3,
|
|
23
|
+
stallBeats: 3
|
|
24
|
+
})
|
|
25
|
+
// "stalled"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The rules run in this order, and each earns its place by naming a different
|
|
29
|
+
response:
|
|
30
|
+
|
|
31
|
+
| Condition | Health | Because |
|
|
32
|
+
| --------------------------------------------- | ---------------- | --------------------------------------------------------------------- |
|
|
33
|
+
| No summary | `unknown` | Nothing to say, and nothing to do. |
|
|
34
|
+
| `failed` | `failing` | The run itself reported the failure. |
|
|
35
|
+
| `completed`, `cancelled` | `healthy` | A finished run needs nothing. |
|
|
36
|
+
| `waiting-approval`, or parked on `approval` | `awaiting-human` | A human owes it an answer. |
|
|
37
|
+
| Parked with no waiting reason | `awaiting-human` | Only an operator's own park writes no reason, so a person stopped it. |
|
|
38
|
+
| `roundOrdinal` at or past `roundBound` | `runaway-loop` | The lineage loops without converging. |
|
|
39
|
+
| The last settled attempt failed | `failing` | The run is alive and its work is not landing. |
|
|
40
|
+
| No progress for `stallBeats`, an attempt open | `wedged-node` | One attempt started and never settled. |
|
|
41
|
+
| No progress for `stallBeats` | `stalled` | Nothing is happening and nothing is in flight. |
|
|
42
|
+
| Anything else | `healthy` | Entries are still arriving. |
|
|
43
|
+
|
|
44
|
+
`awaiting-human` outranks `failing` on purpose. A run parked for approval after
|
|
45
|
+
a failed attempt is waiting for a person, and resuming or cancelling it would
|
|
46
|
+
take the decision away from them. A park with no reason is the same case: the
|
|
47
|
+
engine names every park it makes, so an unnamed one was an operator's, and
|
|
48
|
+
undoing a deliberate act is the worst thing an unattended loop can do.
|
|
49
|
+
|
|
50
|
+
Progress is measured from `flows.engine.attempt-started` and
|
|
51
|
+
`flows.engine.attempt-finished`, which the engine journals as a pair. An excess
|
|
52
|
+
of starts is an attempt still in flight. The classification reads durable
|
|
53
|
+
evidence only, never an in-process fiber, which is what lets a monitor watch a
|
|
54
|
+
run in another process.
|
|
55
|
+
|
|
56
|
+
`roundBound` defaults to 32 rounds.
|
|
57
|
+
|
|
58
|
+
## Beat over the control plane
|
|
59
|
+
|
|
60
|
+
`Monitor.run` requires `Control` and `Journal`, and nothing else. Its first
|
|
61
|
+
beat reads a finite snapshot. Later beats pass the last `event.cursor` as
|
|
62
|
+
`afterCursor` and fold only new events into an open-attempt count and the last
|
|
63
|
+
attempt outcome. Monitor beat and heal records advance the cursor but do not
|
|
64
|
+
count as progress. Providers that omit cursor metadata use `afterSequence`
|
|
65
|
+
after each completed snapshot. The loop retains no historical event array:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const report = yield * Monitor.run({
|
|
69
|
+
runId: "run-17",
|
|
70
|
+
monitorId: "oncall-supervisor",
|
|
71
|
+
intervalMs: 5_000,
|
|
72
|
+
maxChecks: 60,
|
|
73
|
+
stallBeats: 3,
|
|
74
|
+
autoHeal: ["stalled", "wedged-node"]
|
|
75
|
+
})
|
|
76
|
+
// { runId: "run-17", beats: [...], health: "healthy" }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
| Option | Default |
|
|
80
|
+
| ------------ | ------------------------------------------------------ |
|
|
81
|
+
| `monitorId` | `default` |
|
|
82
|
+
| `intervalMs` | 1,000 |
|
|
83
|
+
| `maxChecks` | 10 |
|
|
84
|
+
| `stallBeats` | 3 |
|
|
85
|
+
| `roundBound` | 32 |
|
|
86
|
+
| `autoHeal` | none |
|
|
87
|
+
| `heal` | `Control.resume` and `Control.cancel`, per `remedyFor` |
|
|
88
|
+
|
|
89
|
+
Each beat lists the run, replays its journal, classifies, and records
|
|
90
|
+
`control.monitor.beat` carrying the remedy it is about to attempt, _before_
|
|
91
|
+
applying it, so a monitor that crashes mid-heal leaves the evidence of what it
|
|
92
|
+
decided.
|
|
93
|
+
|
|
94
|
+
The remedy is a second record, `control.monitor.healed`, written only once the
|
|
95
|
+
heal returned an `Accepted` or `AlreadyApplied` receipt. A heal that failed,
|
|
96
|
+
was refused as a `Conflict`, or found the run already `Terminal` must not leave
|
|
97
|
+
a durable record saying the run was healed, and only an applied remedy resets
|
|
98
|
+
the stall evidence. A `Terminal` receipt ends the loop, because there is
|
|
99
|
+
nothing left to remedy.
|
|
100
|
+
|
|
101
|
+
Both records are excluded from the progress measurement. A monitor that counted
|
|
102
|
+
its own bookkeeping as progress could never observe a stall: the beat it wrote
|
|
103
|
+
at the top of the loop would be the new entry it congratulated the run for at
|
|
104
|
+
the bottom.
|
|
105
|
+
|
|
106
|
+
## Choose what it may do
|
|
107
|
+
|
|
108
|
+
`remedyFor` maps a health onto an action, and `autoHeal` decides which of those
|
|
109
|
+
the monitor may actually apply:
|
|
110
|
+
|
|
111
|
+
| Health | Remedy |
|
|
112
|
+
| ------------------------- | -------------------------------------------------------- |
|
|
113
|
+
| `stalled`, `wedged-node` | `resume`: nobody is driving the run, so claim it. |
|
|
114
|
+
| `failing`, `runaway-loop` | `cancel`: it will not get better by being driven harder. |
|
|
115
|
+
| everything else | `none` |
|
|
116
|
+
|
|
117
|
+
`autoHeal` is empty by default, because a monitor that healed by default would
|
|
118
|
+
cancel a run the first time it looked at one.
|
|
119
|
+
|
|
120
|
+
A remedy resets the stall count, so one stall produces one resume rather than
|
|
121
|
+
one per beat.
|
|
122
|
+
|
|
123
|
+
## Two monitors on one run
|
|
124
|
+
|
|
125
|
+
Nothing on the control plane leases a run to one watcher. Two monitors both
|
|
126
|
+
beat and both remedy, so `monitorId` is what makes their evidence tellable
|
|
127
|
+
apart and their remedies distinct:
|
|
128
|
+
|
|
129
|
+
- Every record this monitor writes has source `/control/monitor/<monitorId>`
|
|
130
|
+
and carries `monitorId` in its payload.
|
|
131
|
+
- The built-in remedies key on
|
|
132
|
+
`monitor:<monitorId>:<remedy>:<runId>:<beat>`.
|
|
133
|
+
|
|
134
|
+
A remedy must be idempotent on the control plane. A custom `heal` owes the same
|
|
135
|
+
property:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
const report = yield * Monitor.run({
|
|
139
|
+
runId: "run-17",
|
|
140
|
+
autoHeal: ["failing"],
|
|
141
|
+
heal: ({ runId, health, beat }) =>
|
|
142
|
+
control.cancel({
|
|
143
|
+
runId,
|
|
144
|
+
reason: `page-oncall:${health}`,
|
|
145
|
+
idempotencyKey: `oncall:${runId}:${beat}`
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Alert on what the monitor wrote
|
|
151
|
+
|
|
152
|
+
`Monitor.beatEventType` is a journal kind, so an alert policy in
|
|
153
|
+
[`@smthrs/notifications`](/api/notifications) can detect on it directly:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const rules = {
|
|
157
|
+
defaults: { severity: "warning", owner: "oncall" },
|
|
158
|
+
rules: { "wedged-node": { afterMs: 900_000, runbook: "https://runbook/wedged-runs" } },
|
|
159
|
+
detectors: {
|
|
160
|
+
"wedged-node": { field: "health", value: "wedged-node", eventTypes: [Monitor.beatEventType] }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Narrowing the detector to the beat keeps the heal record, which names the same
|
|
166
|
+
health, from reopening a condition the next beat closed. The complete loop, with
|
|
167
|
+
two real durable runs, is
|
|
168
|
+
[`examples/src/38-monitor-and-alert.ts`](https://github.com/smithersai/smithers/blob/main/examples/src/38-monitor-and-alert.ts).
|
|
169
|
+
|
|
170
|
+
## Where to go next
|
|
171
|
+
|
|
172
|
+
- [Watch a run's events](./watch-a-run.md): the stream a beat reads.
|
|
173
|
+
- [Cancel a run, and restart one](./cancel-and-resume.md): the two default
|
|
174
|
+
remedies.
|
|
175
|
+
- [Test against the control plane](./testing.md): classify is pure, so most of
|
|
176
|
+
a monitor needs no stack at all.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Configure observational health
|
|
2
|
+
|
|
3
|
+
`Health` separates authoritative lifecycle from observed activity, derived health,
|
|
4
|
+
human attention, and reading freshness. A live process is not proof of semantic
|
|
5
|
+
work, and an approval wait always remains an approval wait.
|
|
6
|
+
|
|
7
|
+
Register trusted TypeScript checkers through the native CLI's
|
|
8
|
+
`Application.Config.health`, binding checker IDs to flow IDs. The desktop local
|
|
9
|
+
server accepts the same configuration for role and harness IDs. Pure flow bodies
|
|
10
|
+
and browser requests do not contain executable health functions.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import * as Health from "@smthrs/control/Health"
|
|
14
|
+
import { Effect, Schema } from "effect"
|
|
15
|
+
|
|
16
|
+
const Settings = Schema.Struct({ worker: Schema.String })
|
|
17
|
+
declare const inspectWorker: (id: string) => Effect.Effect<Health.ProbeReport, unknown>
|
|
18
|
+
|
|
19
|
+
const checker: Health.HealthChecker<typeof Settings.Type> = {
|
|
20
|
+
id: "worker.activity",
|
|
21
|
+
configSchema: Settings,
|
|
22
|
+
probe: (_context, config) => inspectWorker(config.worker)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const health: Health.HealthConfig = {
|
|
26
|
+
checkers: [checker],
|
|
27
|
+
bindings: {
|
|
28
|
+
"build/review": {
|
|
29
|
+
checkerId: "worker.activity",
|
|
30
|
+
config: { worker: "reviewer" },
|
|
31
|
+
policy: { intervalMs: 5_000, timeoutMs: 2_000, ttlMs: 20_000, stallAfterMs: 120_000 }
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
limits: { maxSubjects: 128, maxConcurrentProbes: 8 }
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Reports contain `activity` (`working`, `idle`, `needs-input`, or `unknown`) and an
|
|
39
|
+
optional closed reason code. Silence and changing terminal output do not establish
|
|
40
|
+
any of these semantics. The lifecycle defaults return unknown activity. Explicit
|
|
41
|
+
`no-progress` or `unreachable` reasons may mark health unhealthy. Known engine
|
|
42
|
+
timer, event, quota, and approval waits retain their authoritative meaning.
|
|
43
|
+
|
|
44
|
+
A run parked on `released` reports `health: "awaiting-human"`,
|
|
45
|
+
`attention: "needs-resume"`, and reason `released`, whatever a probe says. Its
|
|
46
|
+
owner released executions while still alive, usually because the run lease
|
|
47
|
+
lapsed on a stalled host, and nothing restarts them until an operator runs
|
|
48
|
+
`smthrs runs resume <run>`.
|
|
49
|
+
|
|
50
|
+
## Detect a session waiting on a person
|
|
51
|
+
|
|
52
|
+
`jev.session` is registered in every host, so a binding is the whole opt-in. It is
|
|
53
|
+
registration, not a binding: a host that never names it keeps the lifecycle
|
|
54
|
+
default it had.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import * as Health from "@smthrs/control/Health"
|
|
58
|
+
|
|
59
|
+
const health: Health.HealthConfig = {
|
|
60
|
+
bindings: { terminal: { checkerId: "jev.session", exposeOutput: true } }
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The desktop local server writes that binding itself: with a subscription evaluator configured
|
|
65
|
+
in its process it binds `jev.session` with `exposeOutput: true` for every session
|
|
66
|
+
subject it resolves, so a session's output tail is sent to the gateway with zero
|
|
67
|
+
data retention; without the key its configuration is exactly what a caller passed,
|
|
68
|
+
and a subject a caller bound keeps that caller's checker. The native CLI host
|
|
69
|
+
observes runs rather than sessions, so it never binds this checker.
|
|
70
|
+
|
|
71
|
+
The checker sends the session's `alive`, `exitCode`, and the newest 4 KiB of its
|
|
72
|
+
output to Jev, TypeSafe's decision model, through the Vercel AI Gateway, with zero
|
|
73
|
+
data retention. Jev answers one choice question (`working`, `idle`, `needs-input`)
|
|
74
|
+
and one boolean question about whether the output ends waiting for a person, both
|
|
75
|
+
in one request that returns in about 300 ms. `exposeOutput: true` is required:
|
|
76
|
+
without it the host sends no tail and the probe answers unknown.
|
|
77
|
+
|
|
78
|
+
An answer becomes a report only at confidence 0.7 or above. TypeSafe claims 76%
|
|
79
|
+
agreement with a human rater, so a Jev that is merely leaning is a coin flip
|
|
80
|
+
dressed as a reading: a false `needs-input` pages a person who is not needed, and
|
|
81
|
+
a false `idle` retires an agent that is still working. `needs-input` carries the
|
|
82
|
+
reason `prompt-detected`, which the rollup turns into `attention: "needs-input"`.
|
|
83
|
+
A reading below the floor is Jev's own answer and stays `unknown`.
|
|
84
|
+
|
|
85
|
+
## When Jev cannot answer
|
|
86
|
+
|
|
87
|
+
A host that binds `jev.session` supplies its existing evaluator through
|
|
88
|
+
`Health.makeRegistry(config, kind, evaluator)` or
|
|
89
|
+
`JevSessionChecker.makeJevSessionChecker({ evaluator })`. Native hosts resolve
|
|
90
|
+
that judge from their subscription seats. No separate gateway credential is
|
|
91
|
+
read. Without a judge the probe fails with `unconfigured` and remains
|
|
92
|
+
`probe-error`.
|
|
93
|
+
|
|
94
|
+
An unavailable subscription, a plan refusal, a rate limit, a dead socket, an unreadable body and
|
|
95
|
+
the call's own 45 s deadline fail the probe the same way, with the reason that
|
|
96
|
+
names the fault: `http` carrying the provider's status, `unreachable`, `timeout`,
|
|
97
|
+
or `malformed`. `Health.evaluate` records any failing probe as `outcome: "error"`
|
|
98
|
+
with reason `probe-error` and no report, so the rollup reads the subject `stale`
|
|
99
|
+
with activity `unknown`, health `unknown`, and reason `probe-error`, and the
|
|
100
|
+
binding's `backoff` spaces the retries from five up to sixty seconds. A monitor
|
|
101
|
+
that cannot see is worse when it looks calm, so it says so.
|
|
102
|
+
|
|
103
|
+
Two cases are not Jev failing and keep the lifecycle answer: a session that is no
|
|
104
|
+
longer alive, and a session whose output tail the host did not expose. There is
|
|
105
|
+
nothing to ask about either one.
|
|
106
|
+
|
|
107
|
+
The native host admits at most 128 subjects and eight simultaneous probes by
|
|
108
|
+
default. It scans every five seconds, checks each subject every five seconds,
|
|
109
|
+
times out a probe after two seconds, expires observations after twenty seconds,
|
|
110
|
+
and allows two minutes of missing flow progress before classifying it as stalled.
|
|
111
|
+
Failure backoff starts at five seconds and caps at sixty seconds. Per-binding
|
|
112
|
+
policies and host limits are validated before monitoring starts. Unknown checker
|
|
113
|
+
IDs use the lifecycle fallback; malformed configured policies or checker-specific
|
|
114
|
+
configuration fail startup.
|
|
115
|
+
|
|
116
|
+
The host stamps ownership, lifecycle, timing, and evidence position. Reports from
|
|
117
|
+
a changed owner are discarded. A successful observation is recorded under
|
|
118
|
+
`control.status.observed` before publication. Equal evidence positions are ordered
|
|
119
|
+
by durable journal sequence, never by comparing opaque owner identities. Unchanged
|
|
120
|
+
observations are coalesced and renewed before expiry. The gateway's existing
|
|
121
|
+
run-summary projection carries `statusRollup`, and clients expire its semantic
|
|
122
|
+
activity independently if the host stops answering.
|
|
123
|
+
|
|
124
|
+
Native flow monitoring reads events incrementally; the callback receives at most
|
|
125
|
+
256 recent execution events per beat. Observer/notification bookkeeping does not
|
|
126
|
+
count as progress. Derived gateway projections retain bounded recent health
|
|
127
|
+
observations while preserving their original journal cursor, so repeated probes
|
|
128
|
+
do not exhaust the projection's event budget. Raw run-event history keeps its
|
|
129
|
+
existing resource limits and durable journal retention remains the host's policy.
|
|
130
|
+
|
|
131
|
+
`Health.evaluate` is transport-neutral for other trusted hosts. It returns a
|
|
132
|
+
validated observation as an Effect; the host checks ownership again, commits it,
|
|
133
|
+
and passes `{ observation, sequence }` to `Health.rollup`. `Health.makeRegistry`
|
|
134
|
+
and `registry.resolve(key)` are synchronous constructors. Callback Effects must
|
|
135
|
+
have their service requirements provided before registration.
|
|
136
|
+
|
|
137
|
+
These callbacks are trusted host code, not sandboxed plugins. Read-only context
|
|
138
|
+
types do not constrain captured ambient authority, and cooperative Effect
|
|
139
|
+
interruption cannot preempt arbitrary synchronous JavaScript. Status output omits
|
|
140
|
+
raw exceptions, terminal contents, and arbitrary metrics. Probe outcomes and
|
|
141
|
+
latency are measured by `smithers.health.probes` and
|
|
142
|
+
`smithers.health.probe_duration_ms`; the probe span is `smithers.health.probe`.
|
|
143
|
+
|
|
144
|
+
Checker output never authorizes a control mutation. Production observation starts
|
|
145
|
+
with `autoHeal: []`. Existing Alerts policies can consume recorded status fields
|
|
146
|
+
with an explicitly configured real sink; a status observation alone does not
|
|
147
|
+
claim notification delivery.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# PostgreSQL inventory tests
|
|
2
|
+
|
|
3
|
+
The default unit suite needs no PostgreSQL server. Set `SMITHERS_TEST_PG_URL` to run `test/SqlControlRuntimePostgres.test.ts` against a dedicated test database.
|
|
4
|
+
|
|
5
|
+
`//packages/smithers/control:postgresInventory` runs the same assertions on Linux with the `adapterPostgresDatabase` service and loopback networking. It checks filtered inventory pagination, duplicate and absent IDs, empty filters, large ID sets, and time boundaries.
|
|
6
|
+
|
|
7
|
+
The package `test` target runs the suite twice, on SQLite and on a real PostgreSQL server, and merges the coverage before it checks the 100% floors. The PostgreSQL pass runs this file, so the PostgreSQL-only inventory filter counts toward coverage. Run the same matrix from the package directory with `pnpm run test:matrix`.
|