@songsid/agend 2.1.12 → 2.2.0-alpha.2
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 +4 -2
- package/README.zh-TW.md +4 -2
- package/dist/agent-cli.js +6 -1
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +68 -12
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-token-header.d.ts +13 -0
- package/dist/agent-token-header.js +16 -0
- package/dist/agent-token-header.js.map +1 -0
- package/dist/auth/one-time-code.d.ts +19 -0
- package/dist/auth/one-time-code.js +49 -0
- package/dist/auth/one-time-code.js.map +1 -0
- package/dist/auth-api.d.ts +52 -0
- package/dist/auth-api.js +346 -0
- package/dist/auth-api.js.map +1 -0
- package/dist/backend/antigravity.js +4 -3
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +4 -0
- package/dist/backend/claude-code.js +97 -0
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.js +59 -30
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.js +1 -1
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-agent.d.ts +66 -0
- package/dist/backend/kiro-agent.js +302 -0
- package/dist/backend/kiro-agent.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +80 -0
- package/dist/backend/kiro-identity.js +537 -0
- package/dist/backend/kiro-identity.js.map +1 -0
- package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
- package/dist/backend/kiro-v2-store-worker.js +87 -0
- package/dist/backend/kiro-v2-store-worker.js.map +1 -0
- package/dist/backend/kiro-v2-store.d.ts +54 -0
- package/dist/backend/kiro-v2-store.js +131 -0
- package/dist/backend/kiro-v2-store.js.map +1 -0
- package/dist/backend/kiro-v3-identity.d.ts +5 -0
- package/dist/backend/kiro-v3-identity.js +22 -0
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +90 -7
- package/dist/backend/kiro.js +500 -124
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.js +1 -1
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +51 -3
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-analysis.d.ts +92 -0
- package/dist/cache-analysis.js +147 -0
- package/dist/cache-analysis.js.map +1 -0
- package/dist/cache-ledger.d.ts +108 -0
- package/dist/cache-ledger.js +230 -0
- package/dist/cache-ledger.js.map +1 -0
- package/dist/cache-prices.d.ts +32 -0
- package/dist/cache-prices.js +73 -0
- package/dist/cache-prices.js.map +1 -0
- package/dist/cache-scan.d.ts +39 -0
- package/dist/cache-scan.js +115 -0
- package/dist/cache-scan.js.map +1 -0
- package/dist/cache-service.d.ts +115 -0
- package/dist/cache-service.js +417 -0
- package/dist/cache-service.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +1 -0
- package/dist/channel/adapters/discord.js +29 -10
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +1 -0
- package/dist/channel/adapters/telegram.js +13 -1
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +9 -1
- package/dist/classic-channel-manager.d.ts +18 -10
- package/dist/classic-channel-manager.js +45 -18
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli-entry.d.ts +19 -0
- package/dist/cli-entry.js +36 -0
- package/dist/cli-entry.js.map +1 -0
- package/dist/cli.js +471 -146
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +5 -3
- package/dist/command-table.js +29 -27
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +46 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.d.ts +6 -0
- package/dist/connection-secrets.js +4 -0
- package/dist/connection-secrets.js.map +1 -1
- package/dist/daemon.d.ts +95 -4
- package/dist/daemon.js +748 -111
- package/dist/daemon.js.map +1 -1
- package/dist/darwin-memory.d.ts +6 -1
- package/dist/darwin-memory.js +17 -6
- package/dist/darwin-memory.js.map +1 -1
- package/dist/delivery-consumed-watch.d.ts +85 -0
- package/dist/delivery-consumed-watch.js +244 -0
- package/dist/delivery-consumed-watch.js.map +1 -0
- package/dist/delivery-outbox.d.ts +61 -0
- package/dist/delivery-outbox.js +120 -2
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +14 -5
- package/dist/delivery-reconciliation.js +27 -4
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/delivery-reconciliation.test.js +35 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/event-log.d.ts +4 -1
- package/dist/event-log.js +6 -3
- package/dist/event-log.js.map +1 -1
- package/dist/event-loop-watch.d.ts +28 -0
- package/dist/event-loop-watch.js +75 -3
- package/dist/event-loop-watch.js.map +1 -1
- package/dist/fleet-context.d.ts +22 -2
- package/dist/fleet-control-audit.d.ts +5 -0
- package/dist/fleet-control-audit.js +7 -0
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-level-config.d.ts +2 -2
- package/dist/fleet-level-config.js +28 -1
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +339 -12
- package/dist/fleet-manager.js +2623 -461
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.js +2 -3
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
- package/dist/host-memory.d.ts +4 -0
- package/dist/host-memory.js.map +1 -1
- package/dist/install-lock.d.ts +49 -0
- package/dist/install-lock.js +200 -0
- package/dist/install-lock.js.map +1 -0
- package/dist/instance-config-impact.js +8 -2
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +10 -5
- package/dist/instance-lifecycle.js +162 -53
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +164 -16
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.d.ts +2 -0
- package/dist/login-controller.js +17 -5
- package/dist/login-controller.js.map +1 -1
- package/dist/memory-pressure.d.ts +7 -8
- package/dist/memory-pressure.js +76 -15
- package/dist/memory-pressure.js.map +1 -1
- package/dist/monotonic-budget.d.ts +2 -0
- package/dist/monotonic-budget.js +28 -0
- package/dist/monotonic-budget.js.map +1 -0
- package/dist/needs-you-hub.d.ts +114 -0
- package/dist/needs-you-hub.js +370 -0
- package/dist/needs-you-hub.js.map +1 -0
- package/dist/needs-you-live.d.ts +116 -0
- package/dist/needs-you-live.js +228 -0
- package/dist/needs-you-live.js.map +1 -0
- package/dist/needs-you.d.ts +132 -0
- package/dist/needs-you.js +164 -0
- package/dist/needs-you.js.map +1 -0
- package/dist/node-version-guard.d.ts +30 -0
- package/dist/node-version-guard.js +50 -0
- package/dist/node-version-guard.js.map +1 -0
- package/dist/operator-control-paths.d.ts +4 -0
- package/dist/operator-control-paths.js +24 -0
- package/dist/operator-control-paths.js.map +1 -0
- package/dist/outbound-handlers.d.ts +2 -0
- package/dist/outbound-handlers.js +9 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +5 -5
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/package-preimage.d.ts +26 -0
- package/dist/package-preimage.js +96 -0
- package/dist/package-preimage.js.map +1 -0
- package/dist/profile-control.js +4 -20
- package/dist/profile-control.js.map +1 -1
- package/dist/public-web-gateway.d.ts +18 -0
- package/dist/public-web-gateway.js +141 -0
- package/dist/public-web-gateway.js.map +1 -0
- package/dist/public-web-link.d.ts +53 -0
- package/dist/public-web-link.js +175 -0
- package/dist/public-web-link.js.map +1 -0
- package/dist/quickstart-api.d.ts +8 -1
- package/dist/quickstart-api.js +86 -48
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +1 -0
- package/dist/quickstart.js +16 -6
- package/dist/quickstart.js.map +1 -1
- package/dist/restart-guard.d.ts +52 -0
- package/dist/restart-guard.js +109 -0
- package/dist/restart-guard.js.map +1 -0
- package/dist/scheduler/db.d.ts +7 -1
- package/dist/scheduler/db.js +44 -2
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -1
- package/dist/scheduler/retry-label.d.ts +11 -0
- package/dist/scheduler/retry-label.js +24 -0
- package/dist/scheduler/retry-label.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +55 -2
- package/dist/scheduler/scheduler.js +220 -8
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/types.d.ts +22 -0
- package/dist/scheduler/types.js.map +1 -1
- package/dist/secret-store.d.ts +5 -0
- package/dist/secret-store.js +20 -0
- package/dist/secret-store.js.map +1 -1
- package/dist/service-activation.d.ts +152 -0
- package/dist/service-activation.js +407 -0
- package/dist/service-activation.js.map +1 -0
- package/dist/service-installer.d.ts +63 -1
- package/dist/service-installer.js +141 -16
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +65 -0
- package/dist/service-plan.js +108 -0
- package/dist/service-plan.js.map +1 -0
- package/dist/service-restart-selection.d.ts +6 -5
- package/dist/service-restart-selection.js +6 -5
- package/dist/service-restart-selection.js.map +1 -1
- package/dist/settings-api.d.ts +10 -2
- package/dist/settings-api.js +216 -72
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.d.ts +20 -0
- package/dist/settings-baseline.js +104 -0
- package/dist/settings-baseline.js.map +1 -0
- package/dist/settings-change.d.ts +21 -0
- package/dist/settings-change.js +161 -0
- package/dist/settings-change.js.map +1 -0
- package/dist/settings-confirmation.d.ts +105 -0
- package/dist/settings-confirmation.js +232 -0
- package/dist/settings-confirmation.js.map +1 -0
- package/dist/settings-control.d.ts +41 -0
- package/dist/settings-control.js +294 -0
- package/dist/settings-control.js.map +1 -0
- package/dist/settings-effect.d.ts +24 -0
- package/dist/settings-effect.js +169 -0
- package/dist/settings-effect.js.map +1 -0
- package/dist/settings-http-confirmation.d.ts +29 -0
- package/dist/settings-http-confirmation.js +123 -0
- package/dist/settings-http-confirmation.js.map +1 -0
- package/dist/settings-instance-patch.d.ts +4 -0
- package/dist/settings-instance-patch.js +26 -0
- package/dist/settings-instance-patch.js.map +1 -0
- package/dist/settings-request-capability.d.ts +16 -0
- package/dist/settings-request-capability.js +32 -0
- package/dist/settings-request-capability.js.map +1 -0
- package/dist/settings-transaction.d.ts +65 -0
- package/dist/settings-transaction.js +223 -0
- package/dist/settings-transaction.js.map +1 -0
- package/dist/setup-auth.d.ts +10 -18
- package/dist/setup-auth.js +14 -38
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +75 -18
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-guide.d.ts +13 -0
- package/dist/setup-guide.js +25 -0
- package/dist/setup-guide.js.map +1 -0
- package/dist/setup-host.d.ts +16 -0
- package/dist/setup-host.js +199 -35
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +5 -1
- package/dist/setup-wizard.js.map +1 -1
- package/dist/slash-authz.d.ts +17 -3
- package/dist/slash-authz.js +5 -0
- package/dist/slash-authz.js.map +1 -1
- package/dist/spawn-gate.js +3 -3
- package/dist/spawn-gate.js.map +1 -1
- package/dist/sqlite-open-errors.d.ts +12 -0
- package/dist/sqlite-open-errors.js +13 -0
- package/dist/sqlite-open-errors.js.map +1 -0
- package/dist/statusline-watcher.d.ts +5 -0
- package/dist/statusline-watcher.js +2 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/steer-capability.d.ts +6 -0
- package/dist/steer-capability.js +8 -0
- package/dist/steer-capability.js.map +1 -1
- package/dist/sync-work-attribution.d.ts +14 -0
- package/dist/sync-work-attribution.js +43 -8
- package/dist/sync-work-attribution.js.map +1 -1
- package/dist/tips.js +10 -5
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.d.ts +57 -5
- package/dist/tmux-control.js +379 -61
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +20 -6
- package/dist/tmux-manager.js +90 -47
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +59 -0
- package/dist/tmux-read.js +213 -0
- package/dist/tmux-read.js.map +1 -0
- package/dist/topic-commands.d.ts +21 -10
- package/dist/topic-commands.js +205 -63
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.js +2 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/tunnel/cloudflared-install.d.ts +2 -0
- package/dist/tunnel/cloudflared-install.js +1 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +14 -0
- package/dist/tunnel/purpose-lane.js +43 -0
- package/dist/tunnel/purpose-lane.js.map +1 -0
- package/dist/turn-reply-guard.d.ts +2 -0
- package/dist/turn-reply-guard.js +10 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/ui/app.html +18 -0
- package/dist/ui/chat-commands.js +43 -0
- package/dist/ui/chat-render.js +478 -0
- package/dist/ui/chat-store.js +318 -0
- package/dist/ui/chat-strings.js +89 -0
- package/dist/ui/chat-thread.js +246 -0
- package/dist/ui/chat-tour.js +116 -0
- package/dist/ui/fleet-cache.js +168 -0
- package/dist/ui/fleet-org.js +142 -0
- package/dist/ui/panel-chat.js +504 -0
- package/dist/ui/panel-fleet.js +427 -0
- package/dist/ui/panel-needs.js +202 -0
- package/dist/ui/panel-settings.js +754 -0
- package/dist/ui/preview-panel.js +202 -0
- package/dist/ui/preview.js +270 -0
- package/dist/ui/settings-apply.js +239 -0
- package/dist/ui/settings-confirm.js +186 -0
- package/dist/ui/settings-dialogs.js +546 -0
- package/dist/ui/settings-model.js +277 -0
- package/dist/ui/settings-strings.js +654 -0
- package/dist/ui/settings-wizard.js +140 -0
- package/dist/ui/shared/agend-auth.js +93 -0
- package/dist/ui/shared/app-ctx.js +80 -0
- package/dist/ui/shared/app-html.js +9 -0
- package/dist/ui/shared/app-i18n.js +95 -0
- package/dist/ui/shared/app-nav.js +73 -0
- package/dist/ui/shared/app-needs.js +114 -0
- package/dist/ui/shared/app-route.js +73 -0
- package/dist/ui/shared/app-session.js +66 -0
- package/dist/ui/shared/app-shell.js +346 -0
- package/dist/ui/shared/app-store.js +51 -0
- package/dist/ui/shared/app-stream.js +201 -0
- package/dist/ui/shared/app.css +820 -0
- package/dist/ui/shared/app.js +88 -0
- package/dist/ui/shared/fonts/OFL.txt +92 -0
- package/dist/ui/shared/fonts/README.md +21 -0
- package/dist/ui/shared/fonts/inter.woff2 +0 -0
- package/dist/ui/shared/htm.module.js +1 -0
- package/dist/ui/shared/panel-view.js +655 -0
- package/dist/ui/shared/preact-hooks.module.js +2 -0
- package/dist/ui/shared/preact.module.js +2 -0
- package/dist/ui/shared/signin.js +142 -0
- package/dist/ui/shared/theme.js +29 -0
- package/dist/ui/shared/tokens.css +153 -0
- package/dist/ui/shared/ui-confirm.js +43 -0
- package/dist/ui/shared/ui-dialog.js +45 -0
- package/dist/ui/shared/ui-icons.js +63 -0
- package/dist/ui/shared/ui-menu.js +39 -0
- package/dist/ui/shared/ui-states.js +20 -0
- package/dist/ui/shared/ui-toast.js +18 -0
- package/dist/ui/shared/vendor/htm.LICENSE +202 -0
- package/dist/ui/shared/vendor/preact.LICENSE +21 -0
- package/dist/ui/shared/vendor/vendor.json +39 -0
- package/dist/ui/shared/view-strings.js +309 -0
- package/dist/ui/signin.html +52 -0
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +12 -3
- package/dist/update-check.js.map +1 -1
- package/dist/update-dispatch.d.ts +28 -0
- package/dist/update-dispatch.js +73 -0
- package/dist/update-dispatch.js.map +1 -0
- package/dist/update-install.d.ts +129 -0
- package/dist/update-install.js +262 -0
- package/dist/update-install.js.map +1 -0
- package/dist/upgrade-notices.d.ts +17 -0
- package/dist/upgrade-notices.js +93 -0
- package/dist/upgrade-notices.js.map +1 -0
- package/dist/usage/statusline-usage.d.ts +2 -0
- package/dist/usage/statusline-usage.js +14 -10
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.d.ts +6 -1
- package/dist/view-api.js +62 -51
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +85 -14
- package/dist/web-api.js +557 -77
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +71 -27
- package/dist/web-auth.js +169 -69
- package/dist/web-auth.js.map +1 -1
- package/dist/web-body.d.ts +3 -0
- package/dist/web-body.js +55 -0
- package/dist/web-body.js.map +1 -0
- package/dist/web-channel-echo.d.ts +9 -0
- package/dist/web-channel-echo.js +55 -0
- package/dist/web-channel-echo.js.map +1 -0
- package/dist/web-chat-history.d.ts +125 -0
- package/dist/web-chat-history.js +184 -0
- package/dist/web-chat-history.js.map +1 -0
- package/dist/web-commands.d.ts +77 -0
- package/dist/web-commands.js +81 -0
- package/dist/web-commands.js.map +1 -0
- package/dist/web-continuation.d.ts +7 -0
- package/dist/web-continuation.js +22 -0
- package/dist/web-continuation.js.map +1 -0
- package/dist/web-host-guard.d.ts +34 -0
- package/dist/web-host-guard.js +57 -1
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-login.d.ts +72 -0
- package/dist/web-login.js +132 -0
- package/dist/web-login.js.map +1 -0
- package/dist/web-org.d.ts +55 -0
- package/dist/web-org.js +67 -0
- package/dist/web-org.js.map +1 -0
- package/dist/web-preview.d.ts +77 -0
- package/dist/web-preview.js +240 -0
- package/dist/web-preview.js.map +1 -0
- package/dist/web-request-context.d.ts +10 -0
- package/dist/web-request-context.js +13 -0
- package/dist/web-request-context.js.map +1 -0
- package/dist/web-session.d.ts +167 -0
- package/dist/web-session.js +389 -0
- package/dist/web-session.js.map +1 -0
- package/dist/web-shell-routes.d.ts +59 -0
- package/dist/web-shell-routes.js +96 -0
- package/dist/web-shell-routes.js.map +1 -0
- package/dist/web-upload.d.ts +183 -0
- package/dist/web-upload.js +468 -0
- package/dist/web-upload.js.map +1 -0
- package/dist/website-tips-renderer.d.ts +2 -2
- package/dist/website-tips-renderer.js +3 -3
- package/dist/website-tips-renderer.js.map +1 -1
- package/launcher/agend +110 -0
- package/launcher/agend-agent +110 -0
- package/launcher/agend-agent.cjs +4 -0
- package/launcher/agend.cjs +4 -0
- package/launcher/install-admission.cjs +105 -0
- package/launcher/launch.cjs +64 -0
- package/launcher/postinstall.cjs +114 -0
- package/launcher/run-in-process.cjs +13 -0
- package/launcher/runtime-platform.cjs +86 -0
- package/launcher/runtime-select.cjs +217 -0
- package/package.json +18 -7
- package/scripts/preinstall-guard.cjs +55 -0
- package/templates/launchd.plist.ejs +2 -2
- package/templates/systemd.service.ejs +1 -1
- package/dist/ui/dashboard.html +0 -774
- package/dist/ui/settings.html +0 -2165
- package/dist/ui/view.html +0 -1139
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1386 §5.1–5.4: one world's live "Needs you" message in its General — kept current, without flooding, and never
|
|
3
|
+
* left holding a capability it should not.
|
|
4
|
+
*
|
|
5
|
+
* - **Generations (§5.2a).** Every render is a generation. Its Acknowledge capabilities are minted for it, and the
|
|
6
|
+
* previous generation's are revoked *before the render's first await* — an old button is stale from that moment,
|
|
7
|
+
* whether or not the edit lands. Expiry never edits the message: the message is renewed at least every
|
|
8
|
+
* RENEW_MS.
|
|
9
|
+
* - **Single flight with fences (§5.3a).** One render at a time; a change meanwhile schedules exactly one more. After
|
|
10
|
+
* every await (post/edit/delete ACK) the render continues only if its generation is current, the feature is on,
|
|
11
|
+
* the world exists, the target is the one it started with, and the fleet is not stopping. Otherwise nothing is
|
|
12
|
+
* persisted, its capabilities are revoked, and a late post is deleted.
|
|
13
|
+
* - **Edit vs new post (§5.3).** Changes edit in place (debounced, at most one edit per EDIT_MIN_MS). A new item
|
|
14
|
+
* replaces the message with a new post — at the bottom, so people are notified — at most once per POST_MIN_MS;
|
|
15
|
+
* one inside that window is shown by an edit and posted when the window opens.
|
|
16
|
+
* - **The pointer (§5.4).** Reused only at the same canonical place; a moved target never receives this world's list
|
|
17
|
+
* at its former destination (the old message is deleted best effort, without new content).
|
|
18
|
+
*
|
|
19
|
+
* Every effect is injected; times come from a monotonic clock.
|
|
20
|
+
*/
|
|
21
|
+
import type { NeedsYouItem } from "./needs-you.js";
|
|
22
|
+
export interface LiveTarget {
|
|
23
|
+
chatId: string;
|
|
24
|
+
threadId?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface LivePointer extends LiveTarget {
|
|
27
|
+
messageId: string;
|
|
28
|
+
}
|
|
29
|
+
export interface LiveChoice {
|
|
30
|
+
id: string;
|
|
31
|
+
label: string;
|
|
32
|
+
}
|
|
33
|
+
export interface LiveContent {
|
|
34
|
+
/** Nothing waits on this world: no message is posted for it if none exists yet. */
|
|
35
|
+
empty: boolean;
|
|
36
|
+
text: string;
|
|
37
|
+
/** Delivery items that get an Acknowledge button on this render. */
|
|
38
|
+
ackable: NeedsYouItem[];
|
|
39
|
+
}
|
|
40
|
+
/** Capabilities minted for one render; `bind` fixes the message id once a post returns it. */
|
|
41
|
+
export interface LiveAcks {
|
|
42
|
+
choices: LiveChoice[];
|
|
43
|
+
bind(pointer: LivePointer): void;
|
|
44
|
+
}
|
|
45
|
+
export interface LiveDeps {
|
|
46
|
+
world: string;
|
|
47
|
+
now(): number;
|
|
48
|
+
setTimer(fn: () => void, ms: number): unknown;
|
|
49
|
+
clearTimer(handle: unknown): void;
|
|
50
|
+
/** The current canonical General target, or null when this world has none. */
|
|
51
|
+
target(): LiveTarget | null;
|
|
52
|
+
enabled(): boolean;
|
|
53
|
+
stopping(): boolean;
|
|
54
|
+
worldExists(): boolean;
|
|
55
|
+
/** This world's current content. */
|
|
56
|
+
content(): LiveContent;
|
|
57
|
+
post(target: LiveTarget, text: string, choices: LiveChoice[]): Promise<LivePointer>;
|
|
58
|
+
edit(pointer: LivePointer, text: string, choices: LiveChoice[]): Promise<void>;
|
|
59
|
+
remove(pointer: LivePointer): Promise<void>;
|
|
60
|
+
loadPointer(): LivePointer | null;
|
|
61
|
+
savePointer(pointer: LivePointer | null): void;
|
|
62
|
+
/** Mint Acknowledge capabilities for `generation`, bound to `place` (the message id may follow via bind). */
|
|
63
|
+
mintAcks(generation: number, place: LiveTarget & {
|
|
64
|
+
messageId?: string;
|
|
65
|
+
}, items: NeedsYouItem[]): LiveAcks;
|
|
66
|
+
/** Revoke every Acknowledge capability this world holds (all generations). */
|
|
67
|
+
revokeAcks(): void;
|
|
68
|
+
log(message: string, extra?: Record<string, unknown>): void;
|
|
69
|
+
text: {
|
|
70
|
+
stopped: string;
|
|
71
|
+
off: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export declare const DEBOUNCE_MS = 3000;
|
|
75
|
+
export declare const EDIT_MIN_MS = 10000;
|
|
76
|
+
export declare const POST_MIN_MS = 60000;
|
|
77
|
+
export declare const RENEW_MS: number;
|
|
78
|
+
/** A failed post backs off from POST_MIN_MS, doubling, up to this (#1398 review: never a tight retry loop). */
|
|
79
|
+
export declare const POST_BACKOFF_MAX_MS: number;
|
|
80
|
+
/** Telegram General is topic "1" on input and no thread on the wire: both are the same place. */
|
|
81
|
+
export declare function canonicalTarget(target: LiveTarget): LiveTarget;
|
|
82
|
+
export declare function samePlace(a: LiveTarget, b: LiveTarget): boolean;
|
|
83
|
+
export declare class NeedsYouLiveMessage {
|
|
84
|
+
private readonly deps;
|
|
85
|
+
private generation;
|
|
86
|
+
private pointer;
|
|
87
|
+
private inFlight;
|
|
88
|
+
private rerun;
|
|
89
|
+
private timer;
|
|
90
|
+
private dueAt;
|
|
91
|
+
private lastEditAt;
|
|
92
|
+
/** No post (of any kind: first, moved, new item, edit fallback) before this monotonic time. */
|
|
93
|
+
private nextPostAt;
|
|
94
|
+
private postFailures;
|
|
95
|
+
private postWanted;
|
|
96
|
+
private retired;
|
|
97
|
+
constructor(deps: LiveDeps);
|
|
98
|
+
/** The list changed. `newIds`: an item appeared that this world has not shown before (→ a new post, rate-limited). */
|
|
99
|
+
changed(newIds: boolean): void;
|
|
100
|
+
/** Current generation (tests and the ack handler's generation check). */
|
|
101
|
+
get currentGeneration(): number;
|
|
102
|
+
get currentPointer(): LivePointer | null;
|
|
103
|
+
private requestAt;
|
|
104
|
+
private scheduleRenewal;
|
|
105
|
+
/** One render. Public for tests; normal use goes through changed(). */
|
|
106
|
+
render(): Promise<void>;
|
|
107
|
+
private fenced;
|
|
108
|
+
private renderOnce;
|
|
109
|
+
private postNew;
|
|
110
|
+
/** Shutdown: revoke first, then say so on the message, without buttons. No further renders. */
|
|
111
|
+
retire(): Promise<void>;
|
|
112
|
+
/** Replaced by another renderer (a new adapter under this world's id): stop and revoke, touching nothing remote. */
|
|
113
|
+
abandon(): void;
|
|
114
|
+
/** live_message turned off (or the world removed): revoke, mark the message, forget it. */
|
|
115
|
+
disable(): Promise<void>;
|
|
116
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
export const DEBOUNCE_MS = 3_000;
|
|
2
|
+
export const EDIT_MIN_MS = 10_000;
|
|
3
|
+
export const POST_MIN_MS = 60_000;
|
|
4
|
+
export const RENEW_MS = 10 * 60_000;
|
|
5
|
+
/** A failed post backs off from POST_MIN_MS, doubling, up to this (#1398 review: never a tight retry loop). */
|
|
6
|
+
export const POST_BACKOFF_MAX_MS = 15 * 60_000;
|
|
7
|
+
/** Telegram General is topic "1" on input and no thread on the wire: both are the same place. */
|
|
8
|
+
export function canonicalTarget(target) {
|
|
9
|
+
const thread = target.threadId === undefined || target.threadId === "" || target.threadId === "1" ? undefined : String(target.threadId);
|
|
10
|
+
return thread === undefined ? { chatId: String(target.chatId) } : { chatId: String(target.chatId), threadId: thread };
|
|
11
|
+
}
|
|
12
|
+
export function samePlace(a, b) {
|
|
13
|
+
const x = canonicalTarget(a), y = canonicalTarget(b);
|
|
14
|
+
return x.chatId === y.chatId && x.threadId === y.threadId;
|
|
15
|
+
}
|
|
16
|
+
export class NeedsYouLiveMessage {
|
|
17
|
+
deps;
|
|
18
|
+
generation = 0;
|
|
19
|
+
pointer;
|
|
20
|
+
inFlight = false;
|
|
21
|
+
rerun = false;
|
|
22
|
+
timer = null;
|
|
23
|
+
dueAt = Infinity;
|
|
24
|
+
lastEditAt = -Infinity;
|
|
25
|
+
/** No post (of any kind: first, moved, new item, edit fallback) before this monotonic time. */
|
|
26
|
+
nextPostAt = -Infinity;
|
|
27
|
+
postFailures = 0;
|
|
28
|
+
postWanted = false;
|
|
29
|
+
retired = false;
|
|
30
|
+
constructor(deps) {
|
|
31
|
+
this.deps = deps;
|
|
32
|
+
this.pointer = deps.loadPointer();
|
|
33
|
+
// A restart has no capabilities: render at once so the message gets fresh ones (pre-restart buttons are stale).
|
|
34
|
+
this.requestAt(deps.now());
|
|
35
|
+
}
|
|
36
|
+
/** The list changed. `newIds`: an item appeared that this world has not shown before (→ a new post, rate-limited). */
|
|
37
|
+
changed(newIds) {
|
|
38
|
+
if (this.retired)
|
|
39
|
+
return;
|
|
40
|
+
if (newIds)
|
|
41
|
+
this.postWanted = true;
|
|
42
|
+
const now = this.deps.now();
|
|
43
|
+
let at = now + DEBOUNCE_MS;
|
|
44
|
+
if (this.postWanted)
|
|
45
|
+
at = Math.max(at, Math.min(this.nextPostAt, this.lastEditAt + EDIT_MIN_MS));
|
|
46
|
+
else
|
|
47
|
+
at = Math.max(at, this.lastEditAt + EDIT_MIN_MS);
|
|
48
|
+
this.requestAt(at);
|
|
49
|
+
}
|
|
50
|
+
/** Current generation (tests and the ack handler's generation check). */
|
|
51
|
+
get currentGeneration() { return this.generation; }
|
|
52
|
+
get currentPointer() { return this.pointer; }
|
|
53
|
+
requestAt(at) {
|
|
54
|
+
if (this.retired || at >= this.dueAt)
|
|
55
|
+
return;
|
|
56
|
+
if (this.timer !== null)
|
|
57
|
+
this.deps.clearTimer(this.timer);
|
|
58
|
+
this.dueAt = at;
|
|
59
|
+
this.timer = this.deps.setTimer(() => { this.timer = null; this.dueAt = Infinity; void this.render(); }, Math.max(0, at - this.deps.now()));
|
|
60
|
+
}
|
|
61
|
+
scheduleRenewal() {
|
|
62
|
+
this.requestAt(this.deps.now() + RENEW_MS);
|
|
63
|
+
}
|
|
64
|
+
/** One render. Public for tests; normal use goes through changed(). */
|
|
65
|
+
async render() {
|
|
66
|
+
if (this.retired)
|
|
67
|
+
return;
|
|
68
|
+
if (this.inFlight) {
|
|
69
|
+
this.rerun = true;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.inFlight = true;
|
|
73
|
+
try {
|
|
74
|
+
await this.renderOnce();
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.inFlight = false;
|
|
78
|
+
if (this.rerun && !this.retired) {
|
|
79
|
+
this.rerun = false;
|
|
80
|
+
this.requestAt(this.deps.now());
|
|
81
|
+
}
|
|
82
|
+
else if (!this.retired) {
|
|
83
|
+
// A new item shown by an edit inside the post window is posted when the window opens.
|
|
84
|
+
if (this.postWanted)
|
|
85
|
+
this.requestAt(Math.max(this.deps.now(), this.nextPostAt));
|
|
86
|
+
if (this.timer === null)
|
|
87
|
+
this.scheduleRenewal();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
fenced(generation, target) {
|
|
92
|
+
const now = this.deps.target();
|
|
93
|
+
return generation === this.generation && !this.retired && this.deps.enabled() && !this.deps.stopping()
|
|
94
|
+
&& this.deps.worldExists() && now !== null && samePlace(now, target);
|
|
95
|
+
}
|
|
96
|
+
async renderOnce() {
|
|
97
|
+
const target = this.deps.target();
|
|
98
|
+
if (!this.deps.enabled() || this.deps.stopping() || !this.deps.worldExists() || target === null)
|
|
99
|
+
return;
|
|
100
|
+
const generation = ++this.generation;
|
|
101
|
+
// Revoke before await: every earlier generation's buttons are stale from here on, whatever happens below.
|
|
102
|
+
this.deps.revokeAcks();
|
|
103
|
+
const content = this.deps.content();
|
|
104
|
+
const now = this.deps.now();
|
|
105
|
+
// Nothing to say and nothing said yet: a fleet that never needed anyone posts nothing.
|
|
106
|
+
if (this.pointer === null && content.empty) {
|
|
107
|
+
this.postWanted = false;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const moved = this.pointer !== null && !samePlace(this.pointer, target);
|
|
111
|
+
const mayPost = now >= this.nextPostAt;
|
|
112
|
+
if (this.pointer === null || moved) {
|
|
113
|
+
// Only a post will do. Not yet allowed (a failed one is backing off): try again when it is.
|
|
114
|
+
if (!mayPost) {
|
|
115
|
+
this.postWanted = true;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
await this.postNew(generation, target, content, moved);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (this.postWanted && mayPost) {
|
|
122
|
+
await this.postNew(generation, target, content, false);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const pointer = this.pointer;
|
|
126
|
+
const acks = this.deps.mintAcks(generation, pointer, content.ackable);
|
|
127
|
+
try {
|
|
128
|
+
await this.deps.edit(pointer, content.text, acks.choices);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (!this.fenced(generation, target)) {
|
|
132
|
+
this.deps.revokeAcks();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.deps.revokeAcks();
|
|
136
|
+
if (this.deps.now() < this.nextPostAt) {
|
|
137
|
+
// The fallback post is rate-limited like any other: try again when it may.
|
|
138
|
+
this.deps.log("live message edit failed; will post when allowed", { world: this.deps.world, err: String(err?.message ?? err) });
|
|
139
|
+
this.postWanted = true;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.deps.log("live message edit failed; posting a new one", { world: this.deps.world, err: String(err?.message ?? err) });
|
|
143
|
+
const next = ++this.generation;
|
|
144
|
+
await this.postNew(next, target, this.deps.content(), false);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!this.fenced(generation, target)) {
|
|
148
|
+
this.deps.revokeAcks();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.lastEditAt = this.deps.now();
|
|
152
|
+
}
|
|
153
|
+
async postNew(generation, target, content, moved) {
|
|
154
|
+
const old = this.pointer;
|
|
155
|
+
const acks = this.deps.mintAcks(generation, canonicalTarget(target), content.ackable);
|
|
156
|
+
let sent;
|
|
157
|
+
try {
|
|
158
|
+
sent = await this.deps.post(target, content.text, acks.choices);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
this.deps.revokeAcks();
|
|
162
|
+
this.postFailures++;
|
|
163
|
+
const backoff = Math.min(POST_MIN_MS * 2 ** (this.postFailures - 1), POST_BACKOFF_MAX_MS);
|
|
164
|
+
this.nextPostAt = this.deps.now() + backoff;
|
|
165
|
+
this.postWanted = true; // retried — no sooner than nextPostAt
|
|
166
|
+
this.deps.log("live message post failed", { world: this.deps.world, err: String(err?.message ?? err), retryInMs: backoff });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!this.fenced(generation, target)) {
|
|
170
|
+
// A late ACK: disabled, rebound, superseded or stopping meanwhile. Publish nothing current.
|
|
171
|
+
this.deps.revokeAcks();
|
|
172
|
+
void this.deps.remove(sent).catch(() => { });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
acks.bind(sent);
|
|
176
|
+
this.pointer = sent;
|
|
177
|
+
this.deps.savePointer(sent);
|
|
178
|
+
const at = this.deps.now();
|
|
179
|
+
this.nextPostAt = at + POST_MIN_MS;
|
|
180
|
+
this.postFailures = 0;
|
|
181
|
+
this.lastEditAt = at;
|
|
182
|
+
this.postWanted = false;
|
|
183
|
+
// The previous message goes — without new content, so a moved target's former destination never gets this list.
|
|
184
|
+
if (old !== null && !(old.messageId === sent.messageId && samePlace(old, sent))) {
|
|
185
|
+
void this.deps.remove(old).catch(err => this.deps.log(moved ? "could not remove the live message at its former target" : "could not remove the previous live message", { world: this.deps.world, err: String(err?.message ?? err) }));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Shutdown: revoke first, then say so on the message, without buttons. No further renders. */
|
|
189
|
+
async retire() {
|
|
190
|
+
if (this.retired)
|
|
191
|
+
return;
|
|
192
|
+
this.retired = true;
|
|
193
|
+
if (this.timer !== null) {
|
|
194
|
+
this.deps.clearTimer(this.timer);
|
|
195
|
+
this.timer = null;
|
|
196
|
+
}
|
|
197
|
+
this.deps.revokeAcks();
|
|
198
|
+
this.generation++;
|
|
199
|
+
if (this.pointer)
|
|
200
|
+
await this.deps.edit(this.pointer, this.deps.text.stopped, []).catch(() => { });
|
|
201
|
+
}
|
|
202
|
+
/** Replaced by another renderer (a new adapter under this world's id): stop and revoke, touching nothing remote. */
|
|
203
|
+
abandon() {
|
|
204
|
+
if (this.timer !== null) {
|
|
205
|
+
this.deps.clearTimer(this.timer);
|
|
206
|
+
this.timer = null;
|
|
207
|
+
}
|
|
208
|
+
this.retired = true;
|
|
209
|
+
this.deps.revokeAcks();
|
|
210
|
+
this.generation++;
|
|
211
|
+
}
|
|
212
|
+
/** live_message turned off (or the world removed): revoke, mark the message, forget it. */
|
|
213
|
+
async disable() {
|
|
214
|
+
if (this.timer !== null) {
|
|
215
|
+
this.deps.clearTimer(this.timer);
|
|
216
|
+
this.timer = null;
|
|
217
|
+
}
|
|
218
|
+
this.retired = true;
|
|
219
|
+
this.deps.revokeAcks();
|
|
220
|
+
this.generation++;
|
|
221
|
+
const pointer = this.pointer;
|
|
222
|
+
this.pointer = null;
|
|
223
|
+
this.deps.savePointer(null);
|
|
224
|
+
if (pointer)
|
|
225
|
+
await this.deps.edit(pointer, this.deps.text.off, []).catch(() => { });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=needs-you-live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"needs-you-live.js","sourceRoot":"","sources":["../src/needs-you-live.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1386 "Needs you": one list of everything currently waiting on the person, derived — on demand, never stored —
|
|
3
|
+
* from state the fleet already holds. Design: docs/design/1386-needs-you-inbox.md (§3 sources, §4 derivation).
|
|
4
|
+
*
|
|
5
|
+
* This module is pure: the fleet collects the inputs, this decides the items, which world owns each, what the
|
|
6
|
+
* rendered list is (its signature), and the live chat message's text and links. Nothing here touches a socket,
|
|
7
|
+
* a file or a clock of its own.
|
|
8
|
+
*/
|
|
9
|
+
export type NeedsYouType = "prompt" | "awaiting_input" | "instance" | "delivery";
|
|
10
|
+
export interface NeedsYouItem {
|
|
11
|
+
/** Stable per occurrence: a new episode, crash or delivery is a new id (§3). */
|
|
12
|
+
id: string;
|
|
13
|
+
type: NeedsYouType;
|
|
14
|
+
/** The instance to open; a delivery's is its target. */
|
|
15
|
+
instance: string;
|
|
16
|
+
/** The world (adapter id) that owns the instance, or undefined: listed only in /ui (§5.0). */
|
|
17
|
+
owner?: string;
|
|
18
|
+
/** Which kind of wait, for the title (localized by the renderer, not here). */
|
|
19
|
+
reason: NeedsYouReason;
|
|
20
|
+
/** One line, may be "": a prompt's text, an interaction summary, "source → target · kind". */
|
|
21
|
+
detail: string;
|
|
22
|
+
/** Epoch ms when it started waiting. */
|
|
23
|
+
since: number;
|
|
24
|
+
/** prompt: the nonce the web answers through POST /ui/prompt, its buttons, and where the buttons are. */
|
|
25
|
+
nonce?: string;
|
|
26
|
+
actions?: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
}>;
|
|
30
|
+
promptAt?: {
|
|
31
|
+
adapterId: string;
|
|
32
|
+
chatId: string;
|
|
33
|
+
threadId?: string;
|
|
34
|
+
messageId?: string;
|
|
35
|
+
};
|
|
36
|
+
/** delivery: the outbox row; Acknowledge offered. */
|
|
37
|
+
deliveryId?: string;
|
|
38
|
+
}
|
|
39
|
+
export type NeedsYouReason = "hang" | "exited" | "assist" | "permission" | "dangerous_command" | "login" | "dialog" | "terminal_input" | "auth_paused" | "crashed" | "delivery_uncertain" | "delivery_failed";
|
|
40
|
+
/** A fleet prompt offered on the dashboard (hang / exit-restart / interactive-assist), as the fleet holds it. */
|
|
41
|
+
export interface PromptInput {
|
|
42
|
+
nonce: string;
|
|
43
|
+
prefix: string;
|
|
44
|
+
instance: string;
|
|
45
|
+
text: string;
|
|
46
|
+
actions: Array<{
|
|
47
|
+
id: string;
|
|
48
|
+
label: string;
|
|
49
|
+
}>;
|
|
50
|
+
createdAt: number;
|
|
51
|
+
adapterId: string;
|
|
52
|
+
chatId: string;
|
|
53
|
+
threadId?: string;
|
|
54
|
+
messageId?: string;
|
|
55
|
+
/** interactive-assist only: the interaction wait it was raised for (§3.2). */
|
|
56
|
+
assistFor?: {
|
|
57
|
+
owner: string | null;
|
|
58
|
+
episode: number | null;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface InstanceInput {
|
|
62
|
+
name: string;
|
|
63
|
+
/** presentation state: "awaiting_input" when the CLI waits at its terminal and that is not stale/suspected. */
|
|
64
|
+
state?: string;
|
|
65
|
+
interaction?: {
|
|
66
|
+
kind?: string | null;
|
|
67
|
+
owner?: string | null;
|
|
68
|
+
episode?: number | null;
|
|
69
|
+
since?: number | null;
|
|
70
|
+
} | null;
|
|
71
|
+
interactionSummary?: string | null;
|
|
72
|
+
/** The pause marker's reason, for a paused instance; and when it was paused. */
|
|
73
|
+
pauseReason?: string | null;
|
|
74
|
+
pausedAt?: number | null;
|
|
75
|
+
/** "crashed" with the time the fleet first saw it so (§3.3). */
|
|
76
|
+
crashedAt?: number | null;
|
|
77
|
+
}
|
|
78
|
+
export interface DeliveryInput {
|
|
79
|
+
deliveryId: string;
|
|
80
|
+
state: "uncertain" | "failed";
|
|
81
|
+
source: string;
|
|
82
|
+
target: string;
|
|
83
|
+
kind: string;
|
|
84
|
+
finishedAt: number;
|
|
85
|
+
}
|
|
86
|
+
export interface NeedsYouInputs {
|
|
87
|
+
prompts: PromptInput[];
|
|
88
|
+
instances: InstanceInput[];
|
|
89
|
+
deliveries: DeliveryInput[];
|
|
90
|
+
/** The live world that owns an instance, or undefined (no channel, or its world is gone). */
|
|
91
|
+
ownerOf: (instance: string) => string | undefined;
|
|
92
|
+
}
|
|
93
|
+
/** §3: every item, from the inputs only. Ordered by instance (the one with the oldest item first), then oldest first. */
|
|
94
|
+
export declare function deriveNeedsYou(inputs: NeedsYouInputs): NeedsYouItem[];
|
|
95
|
+
/** §5.0: the items a world's chat surfaces show — only those whose instance it owns. */
|
|
96
|
+
export declare function itemsForWorld(items: readonly NeedsYouItem[], world: string): NeedsYouItem[];
|
|
97
|
+
/** The age bucket shown next to an item: changes of it re-render, nothing finer does. */
|
|
98
|
+
export declare function ageLabel(sinceMs: number, nowMs: number): string;
|
|
99
|
+
/**
|
|
100
|
+
* §4.2: what a surface shows, as a string — ids in order, each item's displayed text and age bucket, and its action
|
|
101
|
+
* coordinates (the prompt message it links to, its owning world). Two lists with the same signature render the same.
|
|
102
|
+
* New-item notification is keyed on ids, separately; a re-render never notifies.
|
|
103
|
+
*/
|
|
104
|
+
export declare function renderSignature(items: readonly NeedsYouItem[], nowMs: number): string;
|
|
105
|
+
export interface WorldPlace {
|
|
106
|
+
type: "discord" | "telegram" | string;
|
|
107
|
+
/** Discord: the guild id. Telegram: the supergroup id (-100…). */
|
|
108
|
+
groupId?: string;
|
|
109
|
+
}
|
|
110
|
+
/** A link to a message (a prompt's own buttons), or undefined when none can be built for this platform/place. */
|
|
111
|
+
export declare function messageLink(place: WorldPlace, at: {
|
|
112
|
+
chatId: string;
|
|
113
|
+
threadId?: string;
|
|
114
|
+
messageId?: string;
|
|
115
|
+
}): string | undefined;
|
|
116
|
+
/** A way to the instance's own thread: Discord's channel mention (clickable in place), Telegram's topic link. */
|
|
117
|
+
export declare function instanceLink(place: WorldPlace, topicId: string | undefined): string | undefined;
|
|
118
|
+
export interface LiveLine {
|
|
119
|
+
item: NeedsYouItem;
|
|
120
|
+
title: string;
|
|
121
|
+
link?: string;
|
|
122
|
+
}
|
|
123
|
+
export declare const LIVE_MAX_LINES = 10;
|
|
124
|
+
export declare const LIVE_MAX_ACKS = 5;
|
|
125
|
+
/**
|
|
126
|
+
* The live message for one world: header, at most LIVE_MAX_LINES lines (then "… and N more"), and which delivery
|
|
127
|
+
* items get an Acknowledge button (the newest LIVE_MAX_ACKS of those shown). `t` localizes.
|
|
128
|
+
*/
|
|
129
|
+
export declare function renderLiveMessage(lines: readonly LiveLine[], nowMs: number, t: (key: string, ...args: Array<string | number>) => string): {
|
|
130
|
+
text: string;
|
|
131
|
+
ackable: NeedsYouItem[];
|
|
132
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1386 "Needs you": one list of everything currently waiting on the person, derived — on demand, never stored —
|
|
3
|
+
* from state the fleet already holds. Design: docs/design/1386-needs-you-inbox.md (§3 sources, §4 derivation).
|
|
4
|
+
*
|
|
5
|
+
* This module is pure: the fleet collects the inputs, this decides the items, which world owns each, what the
|
|
6
|
+
* rendered list is (its signature), and the live chat message's text and links. Nothing here touches a socket,
|
|
7
|
+
* a file or a clock of its own.
|
|
8
|
+
*/
|
|
9
|
+
const PROMPT_REASON = {
|
|
10
|
+
"hang:": "hang",
|
|
11
|
+
"exit-restart:": "exited",
|
|
12
|
+
"interactive-assist:": "assist",
|
|
13
|
+
};
|
|
14
|
+
const INTERACTION_REASON = {
|
|
15
|
+
permission: "permission",
|
|
16
|
+
dangerous_command: "dangerous_command",
|
|
17
|
+
login: "login",
|
|
18
|
+
dialog: "dialog",
|
|
19
|
+
suspected_terminal_input: "terminal_input",
|
|
20
|
+
};
|
|
21
|
+
const oneLine = (text, max = 160) => {
|
|
22
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
23
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
|
|
24
|
+
};
|
|
25
|
+
/** §3: every item, from the inputs only. Ordered by instance (the one with the oldest item first), then oldest first. */
|
|
26
|
+
export function deriveNeedsYou(inputs) {
|
|
27
|
+
const items = [];
|
|
28
|
+
// §3.1 prompts.
|
|
29
|
+
for (const p of inputs.prompts) {
|
|
30
|
+
const reason = PROMPT_REASON[p.prefix];
|
|
31
|
+
if (!reason)
|
|
32
|
+
continue;
|
|
33
|
+
items.push({
|
|
34
|
+
id: `prompt:${p.nonce}`, type: "prompt", instance: p.instance, reason,
|
|
35
|
+
detail: oneLine(p.text), since: p.createdAt, nonce: p.nonce, actions: p.actions,
|
|
36
|
+
promptAt: { adapterId: p.adapterId, chatId: p.chatId, ...(p.threadId !== undefined ? { threadId: p.threadId } : {}), ...(p.messageId ? { messageId: p.messageId } : {}) },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
for (const inst of inputs.instances) {
|
|
40
|
+
// §3.2 awaiting_input — folded into an interactive-assist prompt only when it was raised for this same wait.
|
|
41
|
+
if (inst.state === "awaiting_input" && inst.interaction) {
|
|
42
|
+
const episode = inst.interaction.episode ?? null;
|
|
43
|
+
const owner = inst.interaction.owner ?? null;
|
|
44
|
+
const folded = inputs.prompts.some(p => p.prefix === "interactive-assist:" && p.instance === inst.name
|
|
45
|
+
&& p.assistFor !== undefined && p.assistFor.episode === episode && p.assistFor.owner === owner);
|
|
46
|
+
if (!folded) {
|
|
47
|
+
items.push({
|
|
48
|
+
id: `awaiting:${inst.name}:${owner ?? "-"}:${episode ?? "-"}`, type: "awaiting_input", instance: inst.name,
|
|
49
|
+
reason: INTERACTION_REASON[inst.interaction.kind ?? ""] ?? "dialog",
|
|
50
|
+
detail: oneLine(inst.interactionSummary ?? ""), since: inst.interaction.since ?? 0,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// §3.3 instance: paused for sign-in, or crashed.
|
|
55
|
+
if (inst.pauseReason === "auth") {
|
|
56
|
+
items.push({ id: `auth:${inst.name}:${inst.pausedAt ?? 0}`, type: "instance", instance: inst.name, reason: "auth_paused", detail: "", since: inst.pausedAt ?? 0 });
|
|
57
|
+
}
|
|
58
|
+
if (inst.crashedAt != null) {
|
|
59
|
+
items.push({ id: `crashed:${inst.name}:${inst.crashedAt}`, type: "instance", instance: inst.name, reason: "crashed", detail: "", since: inst.crashedAt });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// §3.4 deliveries (already filtered by the outbox: unacknowledged, last 24 h, newest 50).
|
|
63
|
+
for (const d of inputs.deliveries) {
|
|
64
|
+
items.push({
|
|
65
|
+
id: `delivery:${d.deliveryId}`, type: "delivery", instance: d.target,
|
|
66
|
+
reason: d.state === "uncertain" ? "delivery_uncertain" : "delivery_failed",
|
|
67
|
+
detail: oneLine(`${d.source} → ${d.target} · ${d.kind}`), since: d.finishedAt, deliveryId: d.deliveryId,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
for (const item of items) {
|
|
71
|
+
const owner = inputs.ownerOf(item.instance);
|
|
72
|
+
if (owner !== undefined)
|
|
73
|
+
item.owner = owner;
|
|
74
|
+
}
|
|
75
|
+
// Group by instance; instances ordered by their oldest item, items oldest first.
|
|
76
|
+
const oldest = new Map();
|
|
77
|
+
for (const item of items)
|
|
78
|
+
oldest.set(item.instance, Math.min(oldest.get(item.instance) ?? Infinity, item.since));
|
|
79
|
+
return items.sort((a, b) => (oldest.get(a.instance) - oldest.get(b.instance)) || a.instance.localeCompare(b.instance) || (a.since - b.since) || a.id.localeCompare(b.id));
|
|
80
|
+
}
|
|
81
|
+
/** §5.0: the items a world's chat surfaces show — only those whose instance it owns. */
|
|
82
|
+
export function itemsForWorld(items, world) {
|
|
83
|
+
return items.filter(item => item.owner === world);
|
|
84
|
+
}
|
|
85
|
+
/** The age bucket shown next to an item: changes of it re-render, nothing finer does. */
|
|
86
|
+
export function ageLabel(sinceMs, nowMs) {
|
|
87
|
+
const minutes = Math.max(0, Math.floor((nowMs - sinceMs) / 60_000));
|
|
88
|
+
if (minutes < 1)
|
|
89
|
+
return "<1m";
|
|
90
|
+
if (minutes < 60)
|
|
91
|
+
return `${minutes}m`;
|
|
92
|
+
const hours = Math.floor(minutes / 60);
|
|
93
|
+
return hours < 24 ? `${hours}h` : `${Math.floor(hours / 24)}d`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* §4.2: what a surface shows, as a string — ids in order, each item's displayed text and age bucket, and its action
|
|
97
|
+
* coordinates (the prompt message it links to, its owning world). Two lists with the same signature render the same.
|
|
98
|
+
* New-item notification is keyed on ids, separately; a re-render never notifies.
|
|
99
|
+
*/
|
|
100
|
+
export function renderSignature(items, nowMs) {
|
|
101
|
+
return JSON.stringify(items.map(i => [
|
|
102
|
+
i.id, i.reason, i.detail, ageLabel(i.since, nowMs), i.owner ?? null,
|
|
103
|
+
i.promptAt ? [i.promptAt.adapterId, i.promptAt.chatId, i.promptAt.threadId ?? null, i.promptAt.messageId ?? null] : null,
|
|
104
|
+
]));
|
|
105
|
+
}
|
|
106
|
+
const SNOWFLAKE = /^\d{5,25}$/;
|
|
107
|
+
/** A link to a message (a prompt's own buttons), or undefined when none can be built for this platform/place. */
|
|
108
|
+
export function messageLink(place, at) {
|
|
109
|
+
if (!at.messageId)
|
|
110
|
+
return undefined;
|
|
111
|
+
if (place.type === "discord") {
|
|
112
|
+
const channel = at.threadId ?? at.chatId;
|
|
113
|
+
if (!place.groupId || !SNOWFLAKE.test(place.groupId) || !SNOWFLAKE.test(channel) || !SNOWFLAKE.test(at.messageId))
|
|
114
|
+
return undefined;
|
|
115
|
+
return `https://discord.com/channels/${place.groupId}/${channel}/${at.messageId}`;
|
|
116
|
+
}
|
|
117
|
+
if (place.type === "telegram") {
|
|
118
|
+
const internal = telegramInternalId(at.chatId);
|
|
119
|
+
if (!internal || !/^\d+$/.test(at.messageId))
|
|
120
|
+
return undefined;
|
|
121
|
+
const topic = at.threadId && at.threadId !== "1" && /^\d+$/.test(at.threadId) ? `${at.threadId}/` : "";
|
|
122
|
+
return `https://t.me/c/${internal}/${topic}${at.messageId}`;
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
/** A way to the instance's own thread: Discord's channel mention (clickable in place), Telegram's topic link. */
|
|
127
|
+
export function instanceLink(place, topicId) {
|
|
128
|
+
if (!topicId)
|
|
129
|
+
return undefined;
|
|
130
|
+
if (place.type === "discord")
|
|
131
|
+
return SNOWFLAKE.test(topicId) ? `<#${topicId}>` : undefined;
|
|
132
|
+
if (place.type === "telegram") {
|
|
133
|
+
const internal = telegramInternalId(place.groupId ?? "");
|
|
134
|
+
return internal && /^\d+$/.test(topicId) && topicId !== "1" ? `https://t.me/c/${internal}/${topicId}` : undefined;
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
/** t.me/c/ links take a supergroup's id without its -100 prefix; anything else has no such link. */
|
|
139
|
+
function telegramInternalId(chatId) {
|
|
140
|
+
const m = /^-100(\d+)$/.exec(chatId);
|
|
141
|
+
return m ? m[1] : undefined;
|
|
142
|
+
}
|
|
143
|
+
export const LIVE_MAX_LINES = 10;
|
|
144
|
+
export const LIVE_MAX_ACKS = 5;
|
|
145
|
+
/**
|
|
146
|
+
* The live message for one world: header, at most LIVE_MAX_LINES lines (then "… and N more"), and which delivery
|
|
147
|
+
* items get an Acknowledge button (the newest LIVE_MAX_ACKS of those shown). `t` localizes.
|
|
148
|
+
*/
|
|
149
|
+
export function renderLiveMessage(lines, nowMs, t) {
|
|
150
|
+
if (lines.length === 0)
|
|
151
|
+
return { text: t("needs.live_empty"), ackable: [] };
|
|
152
|
+
const shown = lines.slice(0, LIVE_MAX_LINES);
|
|
153
|
+
const body = shown.map(l => {
|
|
154
|
+
const age = ageLabel(l.item.since, nowMs);
|
|
155
|
+
const head = `• ${l.item.instance} — ${l.title} (${age})`;
|
|
156
|
+
return l.link ? `${head} · ${l.link}` : head;
|
|
157
|
+
});
|
|
158
|
+
if (lines.length > shown.length)
|
|
159
|
+
body.push(t("needs.live_more", lines.length - shown.length));
|
|
160
|
+
const ackable = shown.filter(l => l.item.type === "delivery").map(l => l.item)
|
|
161
|
+
.sort((a, b) => b.since - a.since).slice(0, LIVE_MAX_ACKS);
|
|
162
|
+
return { text: [t("needs.live_header", lines.length), ...body].join("\n"), ackable };
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=needs-you.js.map
|