@cursor/july 0.1.97 → 0.1.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -3
- package/dist/bin/agent-serve.js +6 -7
- package/dist/channels/github/defaults.d.ts.map +1 -1
- package/dist/channels/github/defaults.js +28 -3
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +16 -0
- package/dist/channels/github/progress.d.ts +7 -0
- package/dist/channels/github/progress.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.d.ts +2 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +37 -4
- package/dist/channels/slack/api.d.ts +16 -7
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +29 -13
- package/dist/channels/slack/constants.d.ts +6 -0
- package/dist/channels/slack/constants.d.ts.map +1 -1
- package/dist/channels/slack/constants.js +6 -0
- package/dist/channels/slack/defaults.d.ts +9 -4
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +221 -122
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +3 -3
- package/dist/channels/slack/inbound.d.ts +9 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +14 -0
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/live-delivery.d.ts +6 -1
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +110 -20
- package/dist/channels/slack/progress-delivery.d.ts +28 -0
- package/dist/channels/slack/progress-delivery.d.ts.map +1 -0
- package/dist/channels/slack/progress-delivery.js +115 -0
- package/dist/channels/slack/reasoning-card.d.ts +38 -0
- package/dist/channels/slack/reasoning-card.d.ts.map +1 -0
- package/dist/channels/slack/reasoning-card.js +104 -0
- package/dist/channels/slack/redact.d.ts +2 -0
- package/dist/channels/slack/redact.d.ts.map +1 -0
- package/dist/channels/slack/redact.js +5 -0
- package/dist/channels/slack/reply-options.d.ts +40 -0
- package/dist/channels/slack/reply-options.d.ts.map +1 -0
- package/dist/channels/slack/reply-options.js +150 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +8 -4
- package/dist/channels/slack/stream.d.ts +22 -10
- package/dist/channels/slack/stream.d.ts.map +1 -1
- package/dist/channels/slack/stream.js +9 -16
- package/dist/channels/slack/tool-cards.d.ts +22 -0
- package/dist/channels/slack/tool-cards.d.ts.map +1 -0
- package/dist/channels/slack/tool-cards.js +178 -0
- package/dist/channels/slack/types.d.ts +154 -7
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +6 -6
- package/dist/docs/ab.md +1 -1
- package/dist/docs/assets/{ab.md.DJo5r4R-.js → ab.md.mlVgqvSk.js} +1 -1
- package/dist/docs/assets/{app.B3rWNYE1.js → app.DZ1e0Ycq.js} +1 -1
- package/dist/docs/assets/{building-with-agents.md.DI4mEzlt.js → building-with-agents.md.CUSWxlP_.js} +2 -2
- package/dist/docs/assets/chunks/@localSearchIndexroot.j70vvPL4.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.C1lhqJJR.js → VPLocalSearchBox.l8omwc6D.js} +1 -1
- package/dist/docs/assets/chunks/{theme.B40_SXuv.js → theme.JVcD6gel.js} +2 -2
- package/dist/docs/assets/{evals.md.lfJoEVc8.js → evals.md.CPzDAwoH.js} +1 -1
- package/dist/docs/assets/{guides_github.md.Cnh2mL4a.js → guides_github.md.BtPr9GaP.js} +1 -1
- package/dist/docs/assets/{guides_mcp-oauth.md.CN-6YmTJ.js → guides_mcp-oauth.md.Dp6cDP7f.js} +1 -1
- package/dist/docs/assets/{guides_opentelemetry.md.bmPmkvJu.js → guides_opentelemetry.md.BVTXDCRg.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.VDXQV3ja.js → guides_slack.md.9oHPye9o.js} +19 -3
- package/dist/docs/assets/{guides_slack.md.VDXQV3ja.lean.js → guides_slack.md.9oHPye9o.lean.js} +1 -1
- package/dist/docs/assets/hillclimbing.md.CpTGTCle.js +4 -0
- package/dist/docs/assets/{index.md.CVeRUOeZ.js → index.md.Bb4k8kUm.js} +1 -1
- package/dist/docs/assets/{quickstart.md.Nj_LjW_a.js → quickstart.md.DdQOF7Y8.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.RyZf5OTE.js → reference_cli.md.DfoeyvL0.js} +4 -4
- package/dist/docs/assets/{reference_cli.md.RyZf5OTE.lean.js → reference_cli.md.DfoeyvL0.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.DxldvyIB.js → reference_connections.md.BiGoBAk2.js} +1 -1
- package/dist/docs/assets/{reference_http-api.md.5zOAbV86.js → reference_http-api.md.BEJx9XVj.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.B84gw9Ii.js → reference_tools.md.CvAHsdSp.js} +10 -2
- package/dist/docs/assets/{reference_tools.md.B84gw9Ii.lean.js → reference_tools.md.CvAHsdSp.lean.js} +1 -1
- package/dist/docs/assets/scaffolding-agents.md.em43xlY1.js +1 -0
- package/dist/docs/assets/skills_ab.md.CsFNatVx.js +26 -0
- package/dist/docs/assets/skills_ab.md.CsFNatVx.lean.js +1 -0
- package/dist/docs/assets/skills_create-agent.md.BVoWPcan.js +8 -0
- package/dist/docs/assets/skills_create-agent.md.BVoWPcan.lean.js +1 -0
- package/dist/docs/assets/skills_debug.md.CDbPhHfg.js +1 -0
- package/dist/docs/assets/skills_debug.md.CDbPhHfg.lean.js +1 -0
- package/dist/docs/assets/skills_evals.md.723kpUmA.js +25 -0
- package/dist/docs/assets/skills_evals.md.723kpUmA.lean.js +1 -0
- package/dist/docs/assets/skills_framework-map.md.BTi817yv.js +1 -0
- package/dist/docs/assets/skills_framework-map.md.BTi817yv.lean.js +1 -0
- package/dist/docs/assets/skills_github.md.D0JahM8c.js +16 -0
- package/dist/docs/assets/skills_github.md.D0JahM8c.lean.js +1 -0
- package/dist/docs/assets/skills_hillclimb.md.B_zJerxA.js +7 -0
- package/dist/docs/assets/skills_hillclimb.md.B_zJerxA.lean.js +1 -0
- package/dist/docs/assets/skills_index.md.DKwIxzGg.js +1 -0
- package/dist/docs/assets/skills_index.md.DKwIxzGg.lean.js +1 -0
- package/dist/docs/assets/skills_mcp-auth.md.DGvFP3HE.js +18 -0
- package/dist/docs/assets/skills_mcp-auth.md.DGvFP3HE.lean.js +1 -0
- package/dist/docs/assets/skills_otel.md.CgiZryR3.js +8 -0
- package/dist/docs/assets/skills_otel.md.CgiZryR3.lean.js +1 -0
- package/dist/docs/assets/skills_setup-slack.md.BBgx8lUz.js +20 -0
- package/dist/docs/assets/skills_setup-slack.md.BBgx8lUz.lean.js +1 -0
- package/dist/docs/assets/{troubleshooting.md.DCiPBhYs.js → troubleshooting.md.Cus_YZga.js} +1 -1
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/building-with-agents.md +14 -11
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/design/runtime-abstraction.md +1757 -0
- package/dist/docs/evals.html +6 -6
- package/dist/docs/evals.md +1 -1
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +5 -5
- package/dist/docs/guides/github.md +1 -1
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +5 -5
- package/dist/docs/guides/mcp-oauth.md +1 -1
- package/dist/docs/guides/opentelemetry.html +7 -7
- package/dist/docs/guides/opentelemetry.md +2 -2
- package/dist/docs/guides/slack.html +22 -6
- package/dist/docs/guides/slack.md +80 -1
- package/dist/docs/guides/webhooks.html +5 -5
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +6 -6
- package/dist/docs/hillclimbing.md +5 -5
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +1 -1
- package/dist/docs/llms-full.txt +2830 -37
- package/dist/docs/llms.txt +15 -0
- package/dist/docs/quickstart.html +5 -5
- package/dist/docs/quickstart.md +1 -1
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +7 -7
- package/dist/docs/reference/cli.md +1 -7
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/connections.md +1 -1
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/http-api.md +1 -0
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +14 -6
- package/dist/docs/reference/tools.md +25 -0
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/scaffolding-agents.md +3 -3
- package/dist/docs/skills/ab.html +52 -0
- package/dist/docs/skills/ab.md +50 -0
- package/dist/docs/skills/create-agent.html +34 -0
- package/dist/docs/skills/create-agent.md +160 -0
- package/dist/docs/skills/debug.html +27 -0
- package/dist/docs/skills/debug.md +36 -0
- package/dist/docs/skills/evals.html +51 -0
- package/dist/docs/skills/evals.md +99 -0
- package/dist/docs/skills/framework-map.html +27 -0
- package/dist/docs/skills/framework-map.md +95 -0
- package/dist/docs/skills/github.html +42 -0
- package/dist/docs/skills/github.md +93 -0
- package/dist/docs/skills/hillclimb.html +33 -0
- package/dist/docs/skills/hillclimb.md +55 -0
- package/dist/docs/skills/index.html +27 -0
- package/dist/docs/skills/index.md +21 -0
- package/dist/docs/skills/mcp-auth.html +44 -0
- package/dist/docs/skills/mcp-auth.md +76 -0
- package/dist/docs/skills/otel.html +34 -0
- package/dist/docs/skills/otel.md +48 -0
- package/dist/docs/skills/setup-slack.html +46 -0
- package/dist/docs/skills/setup-slack.md +141 -0
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +4 -4
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +1 -1
- package/dist/files-backends/cursor-hosted.d.ts +9 -2
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +9 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/as-of.d.ts +19 -0
- package/dist/internal/as-of.d.ts.map +1 -0
- package/dist/internal/as-of.js +82 -0
- package/dist/internal/cli-deploy.d.ts +2 -1
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +102 -7
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +3 -4
- package/dist/internal/cursor/hosted-store-secrets.d.ts +15 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-store-secrets.js +48 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +5 -6
- package/dist/internal/deploy-client.d.ts +6 -6
- package/dist/internal/deploy-client.js +7 -7
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +11 -1
- package/dist/internal/framework-file-storage.d.ts +5 -4
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +6 -4
- package/dist/internal/framework-storage-selection.d.ts +13 -11
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +28 -23
- package/dist/internal/hosted-admission-adapter.d.ts +28 -0
- package/dist/internal/hosted-admission-adapter.d.ts.map +1 -0
- package/dist/internal/hosted-admission-adapter.js +7 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +6 -1
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +1 -1
- package/dist/internal/hosted-delivery.d.ts +15 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +47 -21
- package/dist/internal/hosted-execution-diag.d.ts +11 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
- package/dist/internal/hosted-execution-diag.js +39 -2
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +4 -1
- package/dist/internal/server.d.ts +3 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +82 -19
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +24 -4
- package/dist/internal/session-run-log.d.ts +120 -0
- package/dist/internal/session-run-log.d.ts.map +1 -0
- package/dist/internal/session-run-log.js +358 -0
- package/dist/internal/tool-policy.d.ts +20 -8
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +11 -0
- package/dist/playground/assets/index-BszoQDc6.css +1 -0
- package/dist/playground/assets/index-DVs98vPL.js +69 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted-v2.d.ts +83 -0
- package/dist/storage-backends/cursor-hosted-v2.d.ts.map +1 -0
- package/dist/storage-backends/cursor-hosted-v2.js +164 -0
- package/dist/storage-backends/cursor-hosted.d.ts +6 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +6 -1
- package/dist/storage-protocol.d.ts +8 -0
- package/dist/storage-protocol.d.ts.map +1 -1
- package/dist/storage-protocol.js +8 -0
- package/dist/tools.d.ts +12 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +1 -1
- package/docs/ab.md +1 -1
- package/docs/building-with-agents.md +14 -11
- package/docs/design/runtime-abstraction.md +1757 -0
- package/docs/evals.md +1 -1
- package/docs/guides/github.md +1 -1
- package/docs/guides/mcp-oauth.md +1 -1
- package/docs/guides/opentelemetry.md +2 -2
- package/docs/guides/slack.md +80 -1
- package/docs/hillclimbing.md +5 -5
- package/docs/quickstart.md +1 -1
- package/docs/reference/cli.md +1 -7
- package/docs/reference/connections.md +1 -1
- package/docs/reference/http-api.md +1 -0
- package/docs/reference/tools.md +25 -0
- package/docs/scaffolding-agents.md +3 -3
- package/docs/skills/index.md +26 -0
- package/docs/troubleshooting.md +1 -1
- package/package.json +1 -1
- package/src/bin/agent-serve.ts +6 -7
- package/src/channels/github/defaults.ts +36 -3
- package/src/channels/github/github-channel.ts +17 -0
- package/src/channels/github/progress.ts +7 -0
- package/src/channels/origin/origin-channel.ts +74 -10
- package/src/channels/slack/api.ts +35 -17
- package/src/channels/slack/constants.ts +6 -0
- package/src/channels/slack/defaults.ts +253 -144
- package/src/channels/slack/dispatch.ts +10 -1
- package/src/channels/slack/inbound.ts +17 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/live-delivery.ts +135 -22
- package/src/channels/slack/progress-delivery.ts +133 -0
- package/src/channels/slack/reasoning-card.ts +140 -0
- package/src/channels/slack/redact.ts +6 -0
- package/src/channels/slack/reply-options.ts +229 -0
- package/src/channels/slack/slack-channel.ts +7 -2
- package/src/channels/slack/stream.ts +31 -24
- package/src/channels/slack/tool-cards.ts +221 -0
- package/src/channels/slack/types.ts +168 -7
- package/src/files-backends/cursor-hosted.ts +18 -7
- package/src/index.ts +4 -0
- package/src/internal/as-of.ts +107 -0
- package/src/internal/cli-deploy.ts +130 -10
- package/src/internal/conversation-mirror.ts +3 -8
- package/src/internal/cursor/hosted-store-secrets.ts +72 -0
- package/src/internal/cursor-event-relay.ts +5 -6
- package/src/internal/deploy-client.ts +10 -10
- package/src/internal/discovery.ts +9 -1
- package/src/internal/framework-file-storage.ts +6 -4
- package/src/internal/framework-storage-selection.ts +28 -25
- package/src/internal/hosted-admission-adapter.ts +42 -0
- package/src/internal/hosted-delivery-protocol.ts +7 -2
- package/src/internal/hosted-delivery.ts +88 -36
- package/src/internal/hosted-execution-diag.ts +45 -0
- package/src/internal/http-channel.ts +8 -0
- package/src/internal/server.ts +100 -2
- package/src/internal/session-engine.ts +32 -1
- package/src/internal/session-run-log.ts +579 -0
- package/src/internal/tool-policy.ts +31 -9
- package/src/storage-backends/cursor-hosted-v2.ts +219 -0
- package/src/storage-backends/cursor-hosted.ts +6 -1
- package/src/storage-protocol.ts +10 -0
- package/src/tools.ts +12 -2
- package/src/types.ts +30 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +0 -1
- package/dist/docs/assets/hillclimbing.md.DhESf3OO.js +0 -4
- package/dist/docs/assets/scaffolding-agents.md.D7UUkWw0.js +0 -1
- package/dist/playground/assets/index-DrkI6y5O.js +0 -88
- package/dist/playground/assets/index-DzNGwm7q.css +0 -1
- /package/dist/docs/assets/{ab.md.DJo5r4R-.lean.js → ab.md.mlVgqvSk.lean.js} +0 -0
- /package/dist/docs/assets/{building-with-agents.md.DI4mEzlt.lean.js → building-with-agents.md.CUSWxlP_.lean.js} +0 -0
- /package/dist/docs/assets/{evals.md.lfJoEVc8.lean.js → evals.md.CPzDAwoH.lean.js} +0 -0
- /package/dist/docs/assets/{guides_github.md.Cnh2mL4a.lean.js → guides_github.md.BtPr9GaP.lean.js} +0 -0
- /package/dist/docs/assets/{guides_mcp-oauth.md.CN-6YmTJ.lean.js → guides_mcp-oauth.md.Dp6cDP7f.lean.js} +0 -0
- /package/dist/docs/assets/{guides_opentelemetry.md.bmPmkvJu.lean.js → guides_opentelemetry.md.BVTXDCRg.lean.js} +0 -0
- /package/dist/docs/assets/{hillclimbing.md.DhESf3OO.lean.js → hillclimbing.md.CpTGTCle.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.CVeRUOeZ.lean.js → index.md.Bb4k8kUm.lean.js} +0 -0
- /package/dist/docs/assets/{quickstart.md.Nj_LjW_a.lean.js → quickstart.md.DdQOF7Y8.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.DxldvyIB.lean.js → reference_connections.md.BiGoBAk2.lean.js} +0 -0
- /package/dist/docs/assets/{reference_http-api.md.5zOAbV86.lean.js → reference_http-api.md.BEJx9XVj.lean.js} +0 -0
- /package/dist/docs/assets/{scaffolding-agents.md.D7UUkWw0.lean.js → scaffolding-agents.md.em43xlY1.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.DCiPBhYs.lean.js → troubleshooting.md.Cus_YZga.lean.js} +0 -0
|
@@ -8,12 +8,28 @@
|
|
|
8
8
|
* Assistant-pane threads only render stream APIs. Start lazily with the
|
|
9
9
|
* first real markdown/chunks plus recipient ids — empty startStream is
|
|
10
10
|
* rejected or never attaches.
|
|
11
|
+
*
|
|
12
|
+
* Every call speaks `chunks`. Slack pins a stream's mode at start: text
|
|
13
|
+
* opened as bare `markdown_text` rejects later `task_update` appends with
|
|
14
|
+
* `streaming_mode_mismatch`, so assistant text goes out as `markdown_text`
|
|
15
|
+
* chunks and tool steps can land in the same message.
|
|
11
16
|
*/
|
|
12
17
|
|
|
13
18
|
import type { ChannelEventContext } from "../../types.js";
|
|
14
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
appendChatStream,
|
|
21
|
+
postMessage,
|
|
22
|
+
startChatStream,
|
|
23
|
+
stopChatStream,
|
|
24
|
+
} from "./api.js";
|
|
25
|
+
import { SLACK_MARKDOWN_TEXT_MAX_LEN } from "./constants.js";
|
|
15
26
|
import type { SlackLog } from "./log.js";
|
|
16
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
computeStreamAppend,
|
|
29
|
+
markdownChunk,
|
|
30
|
+
type StreamChunk,
|
|
31
|
+
type StreamTaskChunk,
|
|
32
|
+
} from "./stream.js";
|
|
17
33
|
import type { SlackBotToken, SlackChannelState } from "./types.js";
|
|
18
34
|
|
|
19
35
|
type SlackChannel = ChannelEventContext<SlackChannelState>;
|
|
@@ -41,7 +57,7 @@ type StreamArgs = {
|
|
|
41
57
|
/**
|
|
42
58
|
* How the default handlers deliver an in-progress reply. Methods write live
|
|
43
59
|
* delivery state (`streamTs`, `streamChannelId`, `lastPostedText`,
|
|
44
|
-
* `streamingUnavailable`) on `channel.state` directly.
|
|
60
|
+
* `streamBlockText`, `streamingUnavailable`) on `channel.state` directly.
|
|
45
61
|
*/
|
|
46
62
|
export interface LiveDeliveryStrategy {
|
|
47
63
|
readonly inlineToolSteps: boolean;
|
|
@@ -98,9 +114,13 @@ export function createStreamDeliveryStrategy(
|
|
|
98
114
|
if (activeStreamTs(channel) !== null) {
|
|
99
115
|
return true;
|
|
100
116
|
}
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
117
|
+
const openingText = markdown_text ?? "";
|
|
118
|
+
const opening: StreamChunk[] = [];
|
|
119
|
+
if (openingText !== "") {
|
|
120
|
+
opening.push(markdownChunk(openingText));
|
|
121
|
+
}
|
|
122
|
+
opening.push(...(chunks ?? []));
|
|
123
|
+
if (opening.length === 0) {
|
|
104
124
|
return false;
|
|
105
125
|
}
|
|
106
126
|
const log = getLog();
|
|
@@ -108,8 +128,7 @@ export function createStreamDeliveryStrategy(
|
|
|
108
128
|
botToken,
|
|
109
129
|
channel: channelId,
|
|
110
130
|
thread_ts: threadTs,
|
|
111
|
-
|
|
112
|
-
chunks: hasChunks ? chunks : undefined,
|
|
131
|
+
chunks: opening,
|
|
113
132
|
...streamRecipients(channel),
|
|
114
133
|
});
|
|
115
134
|
if (!started.ok) {
|
|
@@ -126,7 +145,9 @@ export function createStreamDeliveryStrategy(
|
|
|
126
145
|
}
|
|
127
146
|
channel.state.streamTs = started.ts;
|
|
128
147
|
channel.state.streamChannelId = started.channel;
|
|
129
|
-
channel.state.
|
|
148
|
+
channel.state.streamFull = false;
|
|
149
|
+
channel.state.lastPostedText = openingText;
|
|
150
|
+
channel.state.streamBlockText = openingText;
|
|
130
151
|
log.info("chat.startStream ok", {
|
|
131
152
|
channel: started.channel,
|
|
132
153
|
stream_ts: started.ts,
|
|
@@ -167,14 +188,14 @@ export function createStreamDeliveryStrategy(
|
|
|
167
188
|
text: string;
|
|
168
189
|
}): Promise<{ ok: boolean; error?: string }> => {
|
|
169
190
|
const appendText = computeStreamAppend(
|
|
170
|
-
args.channel.state.
|
|
191
|
+
args.channel.state.streamBlockText,
|
|
171
192
|
args.text
|
|
172
193
|
);
|
|
173
194
|
const stopped = await stopChatStream({
|
|
174
195
|
botToken,
|
|
175
196
|
channel: args.channelId,
|
|
176
197
|
ts: args.ts,
|
|
177
|
-
|
|
198
|
+
chunks: appendText === "" ? undefined : [markdownChunk(appendText)],
|
|
178
199
|
});
|
|
179
200
|
args.channel.state.streamTs = null;
|
|
180
201
|
if (stopped.ok) {
|
|
@@ -198,11 +219,77 @@ export function createStreamDeliveryStrategy(
|
|
|
198
219
|
channel.state.streamTs = null;
|
|
199
220
|
};
|
|
200
221
|
|
|
222
|
+
/**
|
|
223
|
+
* `msg_too_long` means the message is full: every further append would
|
|
224
|
+
* fail the same way, so appends stop until `finalize` continues the reply
|
|
225
|
+
* in a new message.
|
|
226
|
+
*/
|
|
227
|
+
const noteAppendFailure = (
|
|
228
|
+
channel: SlackChannel,
|
|
229
|
+
result: { error?: string; unsupported?: boolean }
|
|
230
|
+
): void => {
|
|
231
|
+
if (result.unsupported === true) {
|
|
232
|
+
channel.state.streamingUnavailable = true;
|
|
233
|
+
}
|
|
234
|
+
if (result.error === "msg_too_long") {
|
|
235
|
+
channel.state.streamFull = true;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The streamed message is full. Close it as it stands so it stops spinning
|
|
241
|
+
* and post the rest as a new thread message. Most of the answer is on Slack
|
|
242
|
+
* either way, so the reply counts as delivered even if that post fails: a
|
|
243
|
+
* duplicate would be worse than a lost tail.
|
|
244
|
+
*/
|
|
245
|
+
const continueOverflow = async (args: {
|
|
246
|
+
channel: SlackChannel;
|
|
247
|
+
channelId: string;
|
|
248
|
+
ts: string;
|
|
249
|
+
remainder: string;
|
|
250
|
+
sessionId: string;
|
|
251
|
+
}): Promise<void> => {
|
|
252
|
+
const { channel, channelId, ts, remainder, sessionId } = args;
|
|
253
|
+
const log = getLog();
|
|
254
|
+
await stopChatStream({ botToken, channel: channelId, ts });
|
|
255
|
+
channel.state.streamTs = null;
|
|
256
|
+
const threadTs = channel.state.threadTs;
|
|
257
|
+
if (remainder === "" || threadTs === null || threadTs === "") {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const posted = await postMessage({
|
|
262
|
+
botToken,
|
|
263
|
+
channel: channelId,
|
|
264
|
+
text: remainder,
|
|
265
|
+
threadTs,
|
|
266
|
+
plainText: remainder.length >= SLACK_MARKDOWN_TEXT_MAX_LEN,
|
|
267
|
+
});
|
|
268
|
+
channel.state.lastBotMessageTs = posted.ts;
|
|
269
|
+
log.info("reply overflowed the stream; continued in a new message", {
|
|
270
|
+
channel: channelId,
|
|
271
|
+
stream_ts: ts,
|
|
272
|
+
session: sessionId,
|
|
273
|
+
continued_chars: remainder.length,
|
|
274
|
+
});
|
|
275
|
+
} catch (error) {
|
|
276
|
+
log.warn("stream overflow continuation failed", {
|
|
277
|
+
error: error instanceof Error ? error.message : String(error),
|
|
278
|
+
channel: channelId,
|
|
279
|
+
stream_ts: ts,
|
|
280
|
+
session: sessionId,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
201
285
|
return {
|
|
202
286
|
inlineToolSteps: true,
|
|
203
287
|
open,
|
|
204
288
|
|
|
205
289
|
async append({ channel, channelId, text, sessionId }) {
|
|
290
|
+
if (channel.state.streamFull === true) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
206
293
|
const ts = await ensureOpen({
|
|
207
294
|
channel,
|
|
208
295
|
channelId,
|
|
@@ -213,7 +300,7 @@ export function createStreamDeliveryStrategy(
|
|
|
213
300
|
return;
|
|
214
301
|
}
|
|
215
302
|
const appendText = computeStreamAppend(
|
|
216
|
-
channel.state.
|
|
303
|
+
channel.state.streamBlockText,
|
|
217
304
|
text
|
|
218
305
|
);
|
|
219
306
|
if (appendText === "") {
|
|
@@ -223,7 +310,7 @@ export function createStreamDeliveryStrategy(
|
|
|
223
310
|
botToken,
|
|
224
311
|
channel: channelId,
|
|
225
312
|
ts,
|
|
226
|
-
|
|
313
|
+
chunks: [markdownChunk(appendText)],
|
|
227
314
|
});
|
|
228
315
|
if (!result.ok) {
|
|
229
316
|
getLog().warn("appendStream failed", {
|
|
@@ -232,15 +319,17 @@ export function createStreamDeliveryStrategy(
|
|
|
232
319
|
stream_ts: ts,
|
|
233
320
|
session: sessionId,
|
|
234
321
|
});
|
|
235
|
-
|
|
236
|
-
channel.state.streamingUnavailable = true;
|
|
237
|
-
}
|
|
322
|
+
noteAppendFailure(channel, result);
|
|
238
323
|
return;
|
|
239
324
|
}
|
|
240
325
|
channel.state.lastPostedText = text;
|
|
326
|
+
channel.state.streamBlockText = text;
|
|
241
327
|
},
|
|
242
328
|
|
|
243
329
|
async appendChunks({ channel, channelId, chunks, sessionId }) {
|
|
330
|
+
if (channel.state.streamFull === true) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
244
333
|
const hadStream = activeStreamTs(channel) !== null;
|
|
245
334
|
const ts = await ensureOpen({
|
|
246
335
|
channel,
|
|
@@ -260,6 +349,16 @@ export function createStreamDeliveryStrategy(
|
|
|
260
349
|
ts,
|
|
261
350
|
chunks,
|
|
262
351
|
});
|
|
352
|
+
if (!result.ok) {
|
|
353
|
+
getLog().warn("appendStream chunks failed", {
|
|
354
|
+
error: result.error,
|
|
355
|
+
channel: channelId,
|
|
356
|
+
stream_ts: ts,
|
|
357
|
+
session: sessionId,
|
|
358
|
+
chunks: chunks.length,
|
|
359
|
+
});
|
|
360
|
+
noteAppendFailure(channel, result);
|
|
361
|
+
}
|
|
263
362
|
return result.ok;
|
|
264
363
|
},
|
|
265
364
|
|
|
@@ -274,12 +373,15 @@ export function createStreamDeliveryStrategy(
|
|
|
274
373
|
if (ts === null) {
|
|
275
374
|
return { delivered: false };
|
|
276
375
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
376
|
+
// Measured before the stop attempt mutates stream state.
|
|
377
|
+
const remainder = computeStreamAppend(
|
|
378
|
+
channel.state.streamBlockText,
|
|
379
|
+
text
|
|
380
|
+
);
|
|
381
|
+
const stopped =
|
|
382
|
+
channel.state.streamFull === true
|
|
383
|
+
? { ok: false, error: "msg_too_long" }
|
|
384
|
+
: await stopWithSuffix({ channel, channelId, ts, text });
|
|
283
385
|
if (stopped.ok) {
|
|
284
386
|
log.info("reply delivered via stream", {
|
|
285
387
|
channel: channelId,
|
|
@@ -289,6 +391,17 @@ export function createStreamDeliveryStrategy(
|
|
|
289
391
|
});
|
|
290
392
|
return { delivered: true };
|
|
291
393
|
}
|
|
394
|
+
if (stopped.error === "msg_too_long") {
|
|
395
|
+
await continueOverflow({
|
|
396
|
+
channel,
|
|
397
|
+
channelId,
|
|
398
|
+
ts,
|
|
399
|
+
remainder,
|
|
400
|
+
sessionId,
|
|
401
|
+
});
|
|
402
|
+
channel.state.lastPostedText = text;
|
|
403
|
+
return { delivered: true };
|
|
404
|
+
}
|
|
292
405
|
log.warn("chat.stopStream failed; falling back to postMessage", {
|
|
293
406
|
error: stopped.error,
|
|
294
407
|
channel: channelId,
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mid-turn thread progress when `chat.startStream` is unavailable.
|
|
3
|
+
* One editable message; `setThreadStatus` stays on the caller.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ChannelEventContext } from "../../types.js";
|
|
7
|
+
import { deleteMessage, postMessage, updateMessage } from "./api.js";
|
|
8
|
+
import type { SlackLog } from "./log.js";
|
|
9
|
+
import type { SlackBotToken, SlackChannelState } from "./types.js";
|
|
10
|
+
|
|
11
|
+
type SlackChannel = ChannelEventContext<SlackChannelState>;
|
|
12
|
+
|
|
13
|
+
export interface ProgressDeliveryDeps {
|
|
14
|
+
botToken: SlackBotToken | undefined;
|
|
15
|
+
getLog: () => SlackLog;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function activeProgressTs(state: SlackChannelState): string | null {
|
|
19
|
+
const ts = state.progressTs;
|
|
20
|
+
return ts == null || ts === "" ? null : ts;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProgressDelivery {
|
|
24
|
+
upsert(args: { channel: SlackChannel; text: string }): Promise<void>;
|
|
25
|
+
replace(args: { channel: SlackChannel; text: string }): Promise<boolean>;
|
|
26
|
+
clear(args: { channel: SlackChannel }): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createProgressDelivery(
|
|
30
|
+
deps: ProgressDeliveryDeps
|
|
31
|
+
): ProgressDelivery {
|
|
32
|
+
const { botToken, getLog } = deps;
|
|
33
|
+
|
|
34
|
+
const clear: ProgressDelivery["clear"] = async (args) => {
|
|
35
|
+
const state = args.channel.state;
|
|
36
|
+
const ts = activeProgressTs(state);
|
|
37
|
+
state.progressTs = null;
|
|
38
|
+
if (ts === null || state.channelId === null) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const deleted = await deleteMessage({
|
|
42
|
+
botToken,
|
|
43
|
+
channel: state.channelId,
|
|
44
|
+
ts,
|
|
45
|
+
});
|
|
46
|
+
if (!deleted.ok) {
|
|
47
|
+
getLog().warn("progress delete failed", {
|
|
48
|
+
error: deleted.error,
|
|
49
|
+
channel: state.channelId,
|
|
50
|
+
progress_ts: ts,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
async upsert(args) {
|
|
57
|
+
const state = args.channel.state;
|
|
58
|
+
if (
|
|
59
|
+
state.channelId === null ||
|
|
60
|
+
state.threadTs === null ||
|
|
61
|
+
args.text === ""
|
|
62
|
+
) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const existing = activeProgressTs(state);
|
|
66
|
+
if (existing !== null) {
|
|
67
|
+
const updated = await updateMessage({
|
|
68
|
+
botToken,
|
|
69
|
+
channel: state.channelId,
|
|
70
|
+
ts: existing,
|
|
71
|
+
text: args.text,
|
|
72
|
+
});
|
|
73
|
+
if (updated.ok) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
getLog().warn("progress update failed; posting a new message", {
|
|
77
|
+
error: updated.error,
|
|
78
|
+
channel: state.channelId,
|
|
79
|
+
thread_ts: state.threadTs,
|
|
80
|
+
progress_ts: existing,
|
|
81
|
+
});
|
|
82
|
+
await clear({ channel: args.channel });
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const posted = await postMessage({
|
|
86
|
+
botToken,
|
|
87
|
+
channel: state.channelId,
|
|
88
|
+
threadTs: state.threadTs,
|
|
89
|
+
text: args.text,
|
|
90
|
+
});
|
|
91
|
+
state.progressTs = posted.ts;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
getLog().warn(
|
|
94
|
+
`progress post failed: ${
|
|
95
|
+
error instanceof Error ? error.message : String(error)
|
|
96
|
+
}`,
|
|
97
|
+
{
|
|
98
|
+
channel: state.channelId,
|
|
99
|
+
thread_ts: state.threadTs,
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async replace(args) {
|
|
106
|
+
const state = args.channel.state;
|
|
107
|
+
const ts = activeProgressTs(state);
|
|
108
|
+
if (ts === null || state.channelId === null || args.text === "") {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const updated = await updateMessage({
|
|
112
|
+
botToken,
|
|
113
|
+
channel: state.channelId,
|
|
114
|
+
ts,
|
|
115
|
+
text: args.text,
|
|
116
|
+
});
|
|
117
|
+
if (!updated.ok) {
|
|
118
|
+
getLog().warn("progress replace failed", {
|
|
119
|
+
error: updated.error,
|
|
120
|
+
channel: state.channelId,
|
|
121
|
+
progress_ts: ts,
|
|
122
|
+
});
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
state.lastPostedText = args.text;
|
|
126
|
+
state.lastBotMessageTs = ts;
|
|
127
|
+
state.progressTs = null;
|
|
128
|
+
return true;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
clear,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "Thinking" task card that receives the model's reasoning as it streams.
|
|
3
|
+
* Slack appends a card's `details` across updates, so the card is fed
|
|
4
|
+
* deltas: the part of the current reasoning block already on Slack, plus a
|
|
5
|
+
* buffer of what has not gone out yet.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ChannelEventContext } from "../../types.js";
|
|
9
|
+
import type { ResolvedSlackReasoningCard } from "./reply-options.js";
|
|
10
|
+
import { type StreamTaskChunk, truncateCardText } from "./stream.js";
|
|
11
|
+
import type { ReasoningCardState, SlackChannelState } from "./types.js";
|
|
12
|
+
|
|
13
|
+
const FLUSH_MS = 1_000;
|
|
14
|
+
const FLUSH_CHARS = 160;
|
|
15
|
+
const CARD_ID = "reasoning";
|
|
16
|
+
const BLOCK_SEPARATOR = "\n\n";
|
|
17
|
+
|
|
18
|
+
type Channel = ChannelEventContext<SlackChannelState>;
|
|
19
|
+
type CardStatus = "in_progress" | "complete";
|
|
20
|
+
|
|
21
|
+
export interface ReasoningCard {
|
|
22
|
+
/** Buffer the current reasoning block's cumulative text; flushes when due. */
|
|
23
|
+
append(args: {
|
|
24
|
+
channel: Channel;
|
|
25
|
+
sessionId: string;
|
|
26
|
+
text: string;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
/** Send buffered text and set the card status. */
|
|
29
|
+
flush(args: {
|
|
30
|
+
channel: Channel;
|
|
31
|
+
sessionId: string;
|
|
32
|
+
status: CardStatus;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
/** The next reasoning block appends after a blank line. */
|
|
35
|
+
endBlock(channel: Channel): void;
|
|
36
|
+
inProgress(channel: Channel): boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Delivers one chunk into the reply stream; `false` when nothing went out. */
|
|
40
|
+
export type SendCardChunk = (args: {
|
|
41
|
+
channel: Channel;
|
|
42
|
+
sessionId: string;
|
|
43
|
+
chunk: StreamTaskChunk;
|
|
44
|
+
}) => Promise<boolean>;
|
|
45
|
+
|
|
46
|
+
function cardState(state: SlackChannelState): ReasoningCardState {
|
|
47
|
+
if (state.reasoningCard === undefined) {
|
|
48
|
+
state.reasoningCard = {
|
|
49
|
+
status: null,
|
|
50
|
+
blockSent: null,
|
|
51
|
+
pending: "",
|
|
52
|
+
lastFlushMs: null,
|
|
53
|
+
chars: 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return state.reasoningCard;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createReasoningCard(
|
|
60
|
+
card: ResolvedSlackReasoningCard,
|
|
61
|
+
send: SendCardChunk
|
|
62
|
+
): ReasoningCard {
|
|
63
|
+
const flush: ReasoningCard["flush"] = async ({
|
|
64
|
+
channel,
|
|
65
|
+
sessionId,
|
|
66
|
+
status,
|
|
67
|
+
}) => {
|
|
68
|
+
const state = cardState(channel.state);
|
|
69
|
+
if (
|
|
70
|
+
state.pending === "" &&
|
|
71
|
+
(state.status === status || state.status === null)
|
|
72
|
+
) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const chunk: StreamTaskChunk = {
|
|
76
|
+
type: "task_update",
|
|
77
|
+
id: CARD_ID,
|
|
78
|
+
title: card.title,
|
|
79
|
+
status,
|
|
80
|
+
};
|
|
81
|
+
if (state.pending !== "") {
|
|
82
|
+
chunk.details = state.pending;
|
|
83
|
+
}
|
|
84
|
+
// Stamped on the attempt, so a failing stream is retried at most once
|
|
85
|
+
// per flush window instead of once per token.
|
|
86
|
+
state.lastFlushMs = Date.now();
|
|
87
|
+
if (!(await send({ channel, sessionId, chunk }))) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
state.chars += state.pending.length;
|
|
91
|
+
state.pending = "";
|
|
92
|
+
state.status = status;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
flush,
|
|
97
|
+
|
|
98
|
+
async append({ channel, sessionId, text }) {
|
|
99
|
+
const state = cardState(channel.state);
|
|
100
|
+
if (state.chars >= card.maxChars) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const sent = state.blockSent ?? 0;
|
|
104
|
+
let delta: string;
|
|
105
|
+
if (sent > 0 && text.length >= sent) {
|
|
106
|
+
delta = text.slice(sent);
|
|
107
|
+
} else {
|
|
108
|
+
const hasText = state.chars + state.pending.length > 0;
|
|
109
|
+
delta = `${hasText ? BLOCK_SEPARATOR : ""}${text}`;
|
|
110
|
+
}
|
|
111
|
+
state.blockSent = text.length;
|
|
112
|
+
if (delta === "") {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let pending = `${state.pending}${delta}`;
|
|
116
|
+
const room = card.maxChars - state.chars;
|
|
117
|
+
if (pending.length > room) {
|
|
118
|
+
pending = truncateCardText(pending, room);
|
|
119
|
+
state.chars = card.maxChars - pending.length;
|
|
120
|
+
}
|
|
121
|
+
state.pending = pending;
|
|
122
|
+
|
|
123
|
+
const due =
|
|
124
|
+
state.lastFlushMs === null ||
|
|
125
|
+
Date.now() - state.lastFlushMs >= FLUSH_MS ||
|
|
126
|
+
pending.length >= FLUSH_CHARS;
|
|
127
|
+
if (due) {
|
|
128
|
+
await flush({ channel, sessionId, status: "in_progress" });
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
endBlock(channel) {
|
|
133
|
+
cardState(channel.state).blockSent = null;
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
inProgress(channel) {
|
|
137
|
+
return channel.state.reasoningCard?.status === "in_progress";
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|