taskchef 8.6.2 → 8.7.0
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/.codex-plugin/plugin.json +1 -1
- package/README.md +8 -4
- package/assets/README.md +19 -0
- package/assets/taskchef-app-rounded.png +0 -0
- package/assets/taskchef-avatar-square.png +0 -0
- package/assets/taskchef-avatar-square.svg +8 -0
- package/assets/taskchef-sidebar-dark.svg +6 -0
- package/assets/taskchef-sidebar-light.svg +6 -0
- package/assets/taskchef-sidebar.svg +6 -0
- package/docs/advanced-guide.md +20 -29
- package/docs/dashboard-ui.md +15 -0
- package/docs/diagrams/taskchef-next-reply-lookup.drawio +38 -0
- package/docs/diagrams/taskchef-next-reply-lookup.drawio.png +0 -0
- package/docs/diagrams/taskchef-next-storage.drawio +41 -0
- package/docs/diagrams/taskchef-next-storage.drawio.png +0 -0
- package/docs/images/taskchef-pr-icon-comparison.jpg +0 -0
- package/docs/images/taskchef-unified-status-popup.jpg +0 -0
- package/docs/mcp-app.md +261 -6
- package/docs/spec.md +24 -69
- package/docs/taskchef-next-github.md +206 -0
- package/docs/taskchef-next-status-research.md +102 -0
- package/docs/taskchef-next-storage.md +167 -0
- package/docs/workflows.md +4 -2
- package/node_modules/@napi-rs/keyring/LICENSE +21 -0
- package/node_modules/@napi-rs/keyring/README.md +45 -0
- package/node_modules/@napi-rs/keyring/index.d.ts +232 -0
- package/node_modules/@napi-rs/keyring/index.js +706 -0
- package/node_modules/@napi-rs/keyring/keytar.d.ts +53 -0
- package/node_modules/@napi-rs/keyring/keytar.js +26 -0
- package/node_modules/@napi-rs/keyring/package.json +126 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/README.md +3 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/keyring.darwin-arm64.node +0 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/package.json +32 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/README.md +3 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/keyring.darwin-x64.node +0 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/package.json +32 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/keyring.linux-arm64-gnu.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/keyring.linux-arm64-musl.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/keyring.linux-x64-gnu.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/keyring.linux-x64-musl.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/package.json +35 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/README.md +3 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/keyring.win32-arm64-msvc.node +0 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/package.json +32 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/README.md +3 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/keyring.win32-x64-msvc.node +0 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/package.json +32 -0
- package/node_modules/@types/debug/LICENSE +21 -0
- package/node_modules/@types/debug/README.md +69 -0
- package/node_modules/@types/debug/index.d.ts +50 -0
- package/node_modules/@types/debug/package.json +58 -0
- package/node_modules/@types/mdast/LICENSE +21 -0
- package/node_modules/@types/mdast/README.md +15 -0
- package/node_modules/@types/mdast/index.d.ts +1123 -0
- package/node_modules/@types/mdast/package.json +47 -0
- package/node_modules/@types/ms/LICENSE +21 -0
- package/node_modules/@types/ms/README.md +82 -0
- package/node_modules/@types/ms/index.d.ts +63 -0
- package/node_modules/@types/ms/package.json +26 -0
- package/node_modules/@types/unist/LICENSE +21 -0
- package/node_modules/@types/unist/README.md +15 -0
- package/node_modules/@types/unist/index.d.ts +119 -0
- package/node_modules/@types/unist/package.json +60 -0
- package/node_modules/character-entities/index.d.ts +6 -0
- package/node_modules/character-entities/index.js +2132 -0
- package/node_modules/character-entities/license +22 -0
- package/node_modules/character-entities/package.json +78 -0
- package/node_modules/character-entities/readme.md +152 -0
- package/node_modules/decode-named-character-reference/index.d.ts +13 -0
- package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.js +32 -0
- package/node_modules/decode-named-character-reference/index.js +19 -0
- package/node_modules/decode-named-character-reference/license +22 -0
- package/node_modules/decode-named-character-reference/package.json +90 -0
- package/node_modules/decode-named-character-reference/readme.md +136 -0
- package/node_modules/dequal/dist/index.js +86 -0
- package/node_modules/dequal/dist/index.min.js +1 -0
- package/node_modules/dequal/dist/index.mjs +84 -0
- package/node_modules/dequal/index.d.ts +1 -0
- package/node_modules/dequal/license +21 -0
- package/node_modules/dequal/lite/index.d.ts +1 -0
- package/node_modules/dequal/lite/index.js +31 -0
- package/node_modules/dequal/lite/index.min.js +1 -0
- package/node_modules/dequal/lite/index.mjs +29 -0
- package/node_modules/dequal/package.json +57 -0
- package/node_modules/dequal/readme.md +112 -0
- package/node_modules/devlop/lib/default.js +9 -0
- package/node_modules/devlop/lib/development.d.ts +84 -0
- package/node_modules/devlop/lib/development.js +238 -0
- package/node_modules/devlop/license +22 -0
- package/node_modules/devlop/package.json +80 -0
- package/node_modules/devlop/readme.md +360 -0
- package/node_modules/mdast-util-from-markdown/dev/index.d.ts +23 -0
- package/node_modules/mdast-util-from-markdown/dev/index.js +2 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts +53 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts.map +1 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.js +1348 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/types.d.ts +295 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/types.js +2 -0
- package/node_modules/mdast-util-from-markdown/index.d.ts +23 -0
- package/node_modules/mdast-util-from-markdown/index.js +2 -0
- package/node_modules/mdast-util-from-markdown/lib/index.d.ts +53 -0
- package/node_modules/mdast-util-from-markdown/lib/index.js +1177 -0
- package/node_modules/mdast-util-from-markdown/lib/types.d.ts +295 -0
- package/node_modules/mdast-util-from-markdown/lib/types.js +2 -0
- package/node_modules/mdast-util-from-markdown/license +22 -0
- package/node_modules/mdast-util-from-markdown/package.json +139 -0
- package/node_modules/mdast-util-from-markdown/readme.md +537 -0
- package/node_modules/mdast-util-to-string/index.d.ts +2 -0
- package/node_modules/mdast-util-to-string/index.js +5 -0
- package/node_modules/mdast-util-to-string/lib/index.d.ts +31 -0
- package/node_modules/mdast-util-to-string/lib/index.js +108 -0
- package/node_modules/mdast-util-to-string/license +22 -0
- package/node_modules/mdast-util-to-string/package.json +78 -0
- package/node_modules/mdast-util-to-string/readme.md +218 -0
- package/node_modules/micromark/dev/index.d.ts +82 -0
- package/node_modules/micromark/dev/index.d.ts.map +1 -0
- package/node_modules/micromark/dev/index.js +68 -0
- package/node_modules/micromark/dev/lib/compile.d.ts +16 -0
- package/node_modules/micromark/dev/lib/compile.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/compile.js +1162 -0
- package/node_modules/micromark/dev/lib/constructs.d.ts +73 -0
- package/node_modules/micromark/dev/lib/constructs.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/constructs.js +101 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.d.ts +46 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.js +718 -0
- package/node_modules/micromark/dev/lib/initialize/content.d.ts +4 -0
- package/node_modules/micromark/dev/lib/initialize/content.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/content.js +99 -0
- package/node_modules/micromark/dev/lib/initialize/document.d.ts +10 -0
- package/node_modules/micromark/dev/lib/initialize/document.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/document.js +449 -0
- package/node_modules/micromark/dev/lib/initialize/flow.d.ts +4 -0
- package/node_modules/micromark/dev/lib/initialize/flow.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/flow.js +86 -0
- package/node_modules/micromark/dev/lib/initialize/text.d.ts +8 -0
- package/node_modules/micromark/dev/lib/initialize/text.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/text.js +248 -0
- package/node_modules/micromark/dev/lib/parse.d.ts +10 -0
- package/node_modules/micromark/dev/lib/parse.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/parse.js +58 -0
- package/node_modules/micromark/dev/lib/postprocess.d.ts +9 -0
- package/node_modules/micromark/dev/lib/postprocess.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/postprocess.js +19 -0
- package/node_modules/micromark/dev/lib/preprocess.d.ts +13 -0
- package/node_modules/micromark/dev/lib/preprocess.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/preprocess.js +139 -0
- package/node_modules/micromark/dev/stream.d.ts +35 -0
- package/node_modules/micromark/dev/stream.d.ts.map +1 -0
- package/node_modules/micromark/dev/stream.js +270 -0
- package/node_modules/micromark/index.d.ts +82 -0
- package/node_modules/micromark/index.d.ts.map +1 -0
- package/node_modules/micromark/index.js +60 -0
- package/node_modules/micromark/lib/compile.d.ts +16 -0
- package/node_modules/micromark/lib/compile.d.ts.map +1 -0
- package/node_modules/micromark/lib/compile.js +1068 -0
- package/node_modules/micromark/lib/constructs.d.ts +73 -0
- package/node_modules/micromark/lib/constructs.d.ts.map +1 -0
- package/node_modules/micromark/lib/constructs.js +85 -0
- package/node_modules/micromark/lib/create-tokenizer.d.ts +46 -0
- package/node_modules/micromark/lib/create-tokenizer.d.ts.map +1 -0
- package/node_modules/micromark/lib/create-tokenizer.js +613 -0
- package/node_modules/micromark/lib/initialize/content.d.ts +4 -0
- package/node_modules/micromark/lib/initialize/content.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/content.js +79 -0
- package/node_modules/micromark/lib/initialize/document.d.ts +10 -0
- package/node_modules/micromark/lib/initialize/document.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/document.js +370 -0
- package/node_modules/micromark/lib/initialize/flow.d.ts +4 -0
- package/node_modules/micromark/lib/initialize/flow.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/flow.js +58 -0
- package/node_modules/micromark/lib/initialize/text.d.ts +8 -0
- package/node_modules/micromark/lib/initialize/text.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/text.js +216 -0
- package/node_modules/micromark/lib/parse.d.ts +10 -0
- package/node_modules/micromark/lib/parse.d.ts.map +1 -0
- package/node_modules/micromark/lib/parse.js +56 -0
- package/node_modules/micromark/lib/postprocess.d.ts +9 -0
- package/node_modules/micromark/lib/postprocess.d.ts.map +1 -0
- package/node_modules/micromark/lib/postprocess.js +18 -0
- package/node_modules/micromark/lib/preprocess.d.ts +13 -0
- package/node_modules/micromark/lib/preprocess.d.ts.map +1 -0
- package/node_modules/micromark/lib/preprocess.js +112 -0
- package/node_modules/micromark/license +22 -0
- package/node_modules/micromark/package.json +87 -0
- package/node_modules/micromark/readme.md +488 -0
- package/node_modules/micromark/stream.d.ts +35 -0
- package/node_modules/micromark/stream.d.ts.map +1 -0
- package/node_modules/micromark/stream.js +256 -0
- package/node_modules/micromark-core-commonmark/dev/index.d.ts +23 -0
- package/node_modules/micromark-core-commonmark/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/index.js +22 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.js +294 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.js +269 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.js +62 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.js +162 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.js +69 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.js +166 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.js +472 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.js +178 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.js +257 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts +7 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.js +185 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.js +295 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.js +65 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.js +229 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.js +938 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.js +783 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.js +658 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.js +119 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.js +71 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.js +34 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.js +306 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.js +67 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.js +213 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.js +120 -0
- package/node_modules/micromark-core-commonmark/index.d.ts +23 -0
- package/node_modules/micromark-core-commonmark/index.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/index.js +22 -0
- package/node_modules/micromark-core-commonmark/lib/attention.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/attention.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/attention.js +231 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.js +233 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.js +61 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.js +143 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.js +64 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.js +149 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.js +422 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.js +159 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.js +225 -0
- package/node_modules/micromark-core-commonmark/lib/content.d.ts +7 -0
- package/node_modules/micromark-core-commonmark/lib/content.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/content.js +163 -0
- package/node_modules/micromark-core-commonmark/lib/definition.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/definition.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/definition.js +254 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.js +60 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.js +191 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.js +827 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.js +678 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.js +561 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.js +110 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.js +63 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.js +33 -0
- package/node_modules/micromark-core-commonmark/lib/list.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/list.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/list.js +213 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.js +61 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.js +187 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.js +102 -0
- package/node_modules/micromark-core-commonmark/license +22 -0
- package/node_modules/micromark-core-commonmark/package.json +62 -0
- package/node_modules/micromark-core-commonmark/readme.md +171 -0
- package/node_modules/micromark-factory-destination/dev/index.d.ts +42 -0
- package/node_modules/micromark-factory-destination/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-destination/dev/index.js +255 -0
- package/node_modules/micromark-factory-destination/index.d.ts +42 -0
- package/node_modules/micromark-factory-destination/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-destination/index.js +206 -0
- package/node_modules/micromark-factory-destination/license +22 -0
- package/node_modules/micromark-factory-destination/package.json +57 -0
- package/node_modules/micromark-factory-destination/readme.md +234 -0
- package/node_modules/micromark-factory-label/dev/index.d.ts +37 -0
- package/node_modules/micromark-factory-label/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-label/dev/index.js +172 -0
- package/node_modules/micromark-factory-label/index.d.ts +37 -0
- package/node_modules/micromark-factory-label/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-label/index.js +148 -0
- package/node_modules/micromark-factory-label/license +22 -0
- package/node_modules/micromark-factory-label/package.json +60 -0
- package/node_modules/micromark-factory-label/readme.md +224 -0
- package/node_modules/micromark-factory-space/dev/index.d.ts +60 -0
- package/node_modules/micromark-factory-space/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-space/dev/index.js +122 -0
- package/node_modules/micromark-factory-space/index.d.ts +60 -0
- package/node_modules/micromark-factory-space/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-space/index.js +116 -0
- package/node_modules/micromark-factory-space/license +22 -0
- package/node_modules/micromark-factory-space/package.json +46 -0
- package/node_modules/micromark-factory-space/readme.md +225 -0
- package/node_modules/micromark-factory-title/dev/index.d.ts +36 -0
- package/node_modules/micromark-factory-title/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-title/dev/index.js +169 -0
- package/node_modules/micromark-factory-title/index.d.ts +36 -0
- package/node_modules/micromark-factory-title/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-title/index.js +158 -0
- package/node_modules/micromark-factory-title/license +22 -0
- package/node_modules/micromark-factory-title/package.json +58 -0
- package/node_modules/micromark-factory-title/readme.md +229 -0
- package/node_modules/micromark-factory-whitespace/dev/index.d.ts +22 -0
- package/node_modules/micromark-factory-whitespace/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-whitespace/dev/index.js +53 -0
- package/node_modules/micromark-factory-whitespace/index.d.ts +22 -0
- package/node_modules/micromark-factory-whitespace/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-whitespace/index.js +44 -0
- package/node_modules/micromark-factory-whitespace/license +22 -0
- package/node_modules/micromark-factory-whitespace/package.json +57 -0
- package/node_modules/micromark-factory-whitespace/readme.md +205 -0
- package/node_modules/micromark-util-character/dev/index.d.ts +195 -0
- package/node_modules/micromark-util-character/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-character/dev/index.js +252 -0
- package/node_modules/micromark-util-character/index.d.ts +195 -0
- package/node_modules/micromark-util-character/index.d.ts.map +1 -0
- package/node_modules/micromark-util-character/index.js +246 -0
- package/node_modules/micromark-util-character/license +22 -0
- package/node_modules/micromark-util-character/package.json +57 -0
- package/node_modules/micromark-util-character/readme.md +446 -0
- package/node_modules/micromark-util-chunked/dev/index.d.ts +41 -0
- package/node_modules/micromark-util-chunked/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-chunked/dev/index.js +89 -0
- package/node_modules/micromark-util-chunked/index.d.ts +41 -0
- package/node_modules/micromark-util-chunked/index.d.ts.map +1 -0
- package/node_modules/micromark-util-chunked/index.js +81 -0
- package/node_modules/micromark-util-chunked/license +22 -0
- package/node_modules/micromark-util-chunked/package.json +57 -0
- package/node_modules/micromark-util-chunked/readme.md +219 -0
- package/node_modules/micromark-util-classify-character/dev/index.d.ts +18 -0
- package/node_modules/micromark-util-classify-character/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-classify-character/dev/index.js +38 -0
- package/node_modules/micromark-util-classify-character/index.d.ts +18 -0
- package/node_modules/micromark-util-classify-character/index.d.ts.map +1 -0
- package/node_modules/micromark-util-classify-character/index.js +27 -0
- package/node_modules/micromark-util-classify-character/license +22 -0
- package/node_modules/micromark-util-classify-character/package.json +59 -0
- package/node_modules/micromark-util-classify-character/readme.md +205 -0
- package/node_modules/micromark-util-combine-extensions/index.d.ts +22 -0
- package/node_modules/micromark-util-combine-extensions/index.d.ts.map +1 -0
- package/node_modules/micromark-util-combine-extensions/index.js +143 -0
- package/node_modules/micromark-util-combine-extensions/license +22 -0
- package/node_modules/micromark-util-combine-extensions/package.json +52 -0
- package/node_modules/micromark-util-combine-extensions/readme.md +201 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts +16 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.js +42 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts +16 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.js +32 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/license +22 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/package.json +59 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/readme.md +184 -0
- package/node_modules/micromark-util-decode-string/dev/index.d.ts +14 -0
- package/node_modules/micromark-util-decode-string/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-string/dev/index.js +53 -0
- package/node_modules/micromark-util-decode-string/index.d.ts +14 -0
- package/node_modules/micromark-util-decode-string/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-string/index.js +45 -0
- package/node_modules/micromark-util-decode-string/license +22 -0
- package/node_modules/micromark-util-decode-string/package.json +63 -0
- package/node_modules/micromark-util-decode-string/readme.md +179 -0
- package/node_modules/micromark-util-edit-map/dev/index.d.ts +78 -0
- package/node_modules/micromark-util-edit-map/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-edit-map/dev/index.js +237 -0
- package/node_modules/micromark-util-edit-map/index.d.ts +78 -0
- package/node_modules/micromark-util-edit-map/index.d.ts.map +1 -0
- package/node_modules/micromark-util-edit-map/index.js +225 -0
- package/node_modules/micromark-util-edit-map/license +22 -0
- package/node_modules/micromark-util-edit-map/package.json +48 -0
- package/node_modules/micromark-util-edit-map/readme.md +234 -0
- package/node_modules/micromark-util-encode/index.d.ts +14 -0
- package/node_modules/micromark-util-encode/index.d.ts.map +1 -0
- package/node_modules/micromark-util-encode/index.js +33 -0
- package/node_modules/micromark-util-encode/license +22 -0
- package/node_modules/micromark-util-encode/package.json +47 -0
- package/node_modules/micromark-util-encode/readme.md +176 -0
- package/node_modules/micromark-util-html-tag-name/index.d.ts +30 -0
- package/node_modules/micromark-util-html-tag-name/index.d.ts.map +1 -0
- package/node_modules/micromark-util-html-tag-name/index.js +93 -0
- package/node_modules/micromark-util-html-tag-name/license +22 -0
- package/node_modules/micromark-util-html-tag-name/package.json +47 -0
- package/node_modules/micromark-util-html-tag-name/readme.md +193 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts +21 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.js +38 -0
- package/node_modules/micromark-util-normalize-identifier/index.d.ts +21 -0
- package/node_modules/micromark-util-normalize-identifier/index.d.ts.map +1 -0
- package/node_modules/micromark-util-normalize-identifier/index.js +33 -0
- package/node_modules/micromark-util-normalize-identifier/license +22 -0
- package/node_modules/micromark-util-normalize-identifier/package.json +58 -0
- package/node_modules/micromark-util-normalize-identifier/readme.md +187 -0
- package/node_modules/micromark-util-resolve-all/index.d.ts +22 -0
- package/node_modules/micromark-util-resolve-all/index.d.ts.map +1 -0
- package/node_modules/micromark-util-resolve-all/index.js +32 -0
- package/node_modules/micromark-util-resolve-all/license +22 -0
- package/node_modules/micromark-util-resolve-all/package.json +48 -0
- package/node_modules/micromark-util-resolve-all/readme.md +238 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts +36 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.js +124 -0
- package/node_modules/micromark-util-sanitize-uri/index.d.ts +36 -0
- package/node_modules/micromark-util-sanitize-uri/index.d.ts.map +1 -0
- package/node_modules/micromark-util-sanitize-uri/index.js +107 -0
- package/node_modules/micromark-util-sanitize-uri/license +22 -0
- package/node_modules/micromark-util-sanitize-uri/package.json +59 -0
- package/node_modules/micromark-util-sanitize-uri/readme.md +214 -0
- package/node_modules/micromark-util-subtokenize/dev/index.d.ts +12 -0
- package/node_modules/micromark-util-subtokenize/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/dev/index.js +284 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts +161 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.js +288 -0
- package/node_modules/micromark-util-subtokenize/index.d.ts +12 -0
- package/node_modules/micromark-util-subtokenize/index.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/index.js +228 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts +161 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.js +251 -0
- package/node_modules/micromark-util-subtokenize/license +22 -0
- package/node_modules/micromark-util-subtokenize/package.json +60 -0
- package/node_modules/micromark-util-subtokenize/readme.md +181 -0
- package/node_modules/micromark-util-symbol/lib/codes.d.ts +139 -0
- package/node_modules/micromark-util-symbol/lib/codes.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/codes.js +158 -0
- package/node_modules/micromark-util-symbol/lib/constants.d.ts +37 -0
- package/node_modules/micromark-util-symbol/lib/constants.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/constants.js +44 -0
- package/node_modules/micromark-util-symbol/lib/default.d.ts +5 -0
- package/node_modules/micromark-util-symbol/lib/default.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/default.js +4 -0
- package/node_modules/micromark-util-symbol/lib/types.d.ts +106 -0
- package/node_modules/micromark-util-symbol/lib/types.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/types.js +453 -0
- package/node_modules/micromark-util-symbol/lib/values.d.ts +102 -0
- package/node_modules/micromark-util-symbol/lib/values.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/values.js +109 -0
- package/node_modules/micromark-util-symbol/license +22 -0
- package/node_modules/micromark-util-symbol/package.json +43 -0
- package/node_modules/micromark-util-symbol/readme.md +168 -0
- package/node_modules/micromark-util-types/index.d.ts +1315 -0
- package/node_modules/micromark-util-types/index.js +2 -0
- package/node_modules/micromark-util-types/license +22 -0
- package/node_modules/micromark-util-types/package.json +37 -0
- package/node_modules/micromark-util-types/readme.md +151 -0
- package/node_modules/smol-toml/LICENSE +24 -0
- package/node_modules/smol-toml/README.md +605 -0
- package/node_modules/smol-toml/dist/date.d.ts +41 -0
- package/node_modules/smol-toml/dist/date.js +156 -0
- package/node_modules/smol-toml/dist/error.d.ts +37 -0
- package/node_modules/smol-toml/dist/error.js +67 -0
- package/node_modules/smol-toml/dist/extract.js +289 -0
- package/node_modules/smol-toml/dist/index.cjs +770 -0
- package/node_modules/smol-toml/dist/index.d.ts +44 -0
- package/node_modules/smol-toml/dist/index.js +34 -0
- package/node_modules/smol-toml/dist/parse.d.ts +73 -0
- package/node_modules/smol-toml/dist/parse.js +164 -0
- package/node_modules/smol-toml/dist/primitive.js +151 -0
- package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
- package/node_modules/smol-toml/dist/stringify.js +219 -0
- package/node_modules/smol-toml/dist/struct.js +156 -0
- package/node_modules/smol-toml/dist/util.d.ts +43 -0
- package/node_modules/smol-toml/dist/util.js +57 -0
- package/node_modules/smol-toml/package.json +68 -0
- package/node_modules/unist-util-stringify-position/index.d.ts +1 -0
- package/node_modules/unist-util-stringify-position/index.js +1 -0
- package/node_modules/unist-util-stringify-position/lib/index.d.ts +61 -0
- package/node_modules/unist-util-stringify-position/lib/index.js +84 -0
- package/node_modules/unist-util-stringify-position/license +22 -0
- package/node_modules/unist-util-stringify-position/package.json +80 -0
- package/node_modules/unist-util-stringify-position/readme.md +206 -0
- package/package.json +27 -8
- package/scripts/prepare-github-keyring.js +29 -0
- package/src/codex-app.js +20 -2
- package/src/codex-pricing.js +79 -0
- package/src/codex-projects.js +90 -0
- package/src/codex-session-scanner.js +748 -0
- package/src/codex-session-usage.js +332 -0
- package/src/dashboard/app.js +2 -2
- package/src/dashboard/dist/app.js +135 -60
- package/src/dashboard/dist/app.js.map +1 -1
- package/src/dashboard/dist/styles.css +1 -1
- package/src/dashboard/react/components/AnimatedUsage.test.tsx +78 -0
- package/src/dashboard/react/components/CardStatusIcons.test.tsx +146 -0
- package/src/dashboard/react/components/CardStatusIcons.tsx +138 -0
- package/src/dashboard/react/components/ImageGallery.tsx +22 -0
- package/src/dashboard/react/components/ModelSettings.test.tsx +4 -4
- package/src/dashboard/react/components/ModelSettings.tsx +1 -1
- package/src/dashboard/react/components/OpenChatButton.test.tsx +19 -0
- package/src/dashboard/react/components/OpenChatButton.tsx +2 -1
- package/src/dashboard/react/components/PrGlyph.tsx +35 -0
- package/src/dashboard/react/components/PullRequestBadges.tsx +18 -0
- package/src/dashboard/react/components/QueuedPrompts.test.tsx +42 -0
- package/src/dashboard/react/components/QueuedPrompts.tsx +34 -0
- package/src/dashboard/react/components/RelativeTime.test.tsx +100 -0
- package/src/dashboard/react/components/RelativeTime.tsx +46 -11
- package/src/dashboard/react/components/ReplyCover.test.tsx +30 -0
- package/src/dashboard/react/components/ReplyCover.tsx +33 -0
- package/src/dashboard/react/components/ReplyImagePreview.test.tsx +90 -0
- package/src/dashboard/react/components/ReplyImagePreview.tsx +34 -0
- package/src/dashboard/react/components/ReplyMarkdown.test.tsx +108 -0
- package/src/dashboard/react/components/ReplyMarkdown.tsx +112 -0
- package/src/dashboard/react/components/SavedUsageFormat.test.tsx +11 -0
- package/src/dashboard/react/components/SavedUsageFormat.tsx +68 -0
- package/src/dashboard/react/components/StatusBadge.tsx +2 -2
- package/src/dashboard/react/components/TaskBoard.test.tsx +256 -1
- package/src/dashboard/react/components/TaskBoard.tsx +136 -28
- package/src/dashboard/react/components/TaskCard.tsx +7 -4
- package/src/dashboard/react/components/TaskDetail.test.tsx +92 -1
- package/src/dashboard/react/components/TaskDetail.tsx +37 -10
- package/src/dashboard/react/components/TurnContent.test.tsx +44 -0
- package/src/dashboard/react/components/TurnContent.tsx +53 -0
- package/src/dashboard/react/components/UsagePanel.tsx +2 -2
- package/src/dashboard/react/image-cache.test.ts +23 -0
- package/src/dashboard/react/image-cache.ts +33 -0
- package/src/dashboard/react/presentation.test.tsx +31 -0
- package/src/dashboard/react/styles.css +126 -2
- package/src/dashboard/react/types.ts +55 -1
- package/src/dashboard/time.js +39 -0
- package/src/dashboard.js +4 -3
- package/src/data/codex-pricing.json +537 -0
- package/src/mcp-app/dist/index.html +216 -112
- package/src/mcp-app/react/AppearancePicker.test.tsx +50 -0
- package/src/mcp-app/react/AppearancePicker.tsx +56 -0
- package/src/mcp-app/react/GitHubConnection.test.tsx +78 -0
- package/src/mcp-app/react/GitHubConnection.tsx +78 -0
- package/src/mcp-app/react/NextNotificationCenter.test.tsx +121 -0
- package/src/mcp-app/react/NextNotificationCenter.tsx +94 -0
- package/src/mcp-app/react/NextTaskDetail.test.tsx +124 -0
- package/src/mcp-app/react/NextTaskDetail.tsx +147 -0
- package/src/mcp-app/react/ProjectPicker.tsx +49 -0
- package/src/mcp-app/react/main.test.tsx +499 -49
- package/src/mcp-app/react/main.tsx +359 -86
- package/src/mcp-app/react/styles.css +186 -9
- package/src/mcp-app/react/themes.test.ts +34 -0
- package/src/mcp-app/react/themes.ts +112 -0
- package/src/mcp-app.js +238 -120
- package/src/mcp.js +8 -1
- package/src/next-cache.js +16 -0
- package/src/next-github.js +273 -0
- package/src/next-notifications.js +90 -0
- package/src/recorded-usage-tracker.js +92 -0
- package/src/reply-image.js +65 -0
- package/src/usage-tracker.js +7 -6
- package/src/usage.js +7 -355
- package/src/user-prompt.js +55 -0
- package/src/version.js +0 -1
- package/node_modules/proper-lockfile/CHANGELOG.md +0 -108
- package/node_modules/which/CHANGELOG.md +0 -166
- package/src/resolve-ccusage.js +0 -17
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { normalizeUri } from "micromark-util-sanitize-uri";
|
|
3
|
+
import { displayUserPrompt, responseAnnotations } from "./user-prompt.js";
|
|
4
|
+
import { CodexProjects } from "./codex-projects.js";
|
|
5
|
+
import { CodexSessionUsage } from "./codex-session-usage.js";
|
|
6
|
+
import { CodexPricing } from "./codex-pricing.js";
|
|
7
|
+
import { replyImage, replyImages, localReplyImage } from "./reply-image.js";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { basename, isAbsolute, join } from "node:path";
|
|
10
|
+
import { open, stat, readFile, readdir, mkdir } from "node:fs/promises";
|
|
11
|
+
import { acquireWorkspaceLock } from "./workspace.js";
|
|
12
|
+
import { writeDurableAtomic } from "./state-store.js";
|
|
13
|
+
import { dirname } from "node:path";
|
|
14
|
+
import { parse as parseToml } from "smol-toml";
|
|
15
|
+
|
|
16
|
+
const HEAD_BYTES = 32 * 1024;
|
|
17
|
+
const META_LINE_BYTES = 1024 * 1024;
|
|
18
|
+
const TAIL_BYTES = 256 * 1024;
|
|
19
|
+
const ACTIVE_WINDOW_MS = 2 * 60_000;
|
|
20
|
+
let sqliteModule;
|
|
21
|
+
class UnsupportedNodeVersionError extends Error {}
|
|
22
|
+
class DoneStateError extends Error {}
|
|
23
|
+
class InvalidRolloutMetadataError extends Error {}
|
|
24
|
+
class ProjectMetadataError extends Error {}
|
|
25
|
+
|
|
26
|
+
function supportsReadOnlySqlite(version) {
|
|
27
|
+
const match = /^(\d+)\.(\d+)\./.exec(version);
|
|
28
|
+
if (!match) return false;
|
|
29
|
+
const major = Number(match[1]);
|
|
30
|
+
const minor = Number(match[2]);
|
|
31
|
+
return major > 23 || (major === 23 && minor >= 2) || (major === 22 && minor >= 18);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function sqlite() {
|
|
35
|
+
if (sqliteModule === undefined) sqliteModule = import("node:sqlite");
|
|
36
|
+
return sqliteModule;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function iso(ms) { return new Date(ms).toISOString(); }
|
|
40
|
+
|
|
41
|
+
async function readSchedules(codexHome) {
|
|
42
|
+
const schedules = new Map();
|
|
43
|
+
let errors = 0;
|
|
44
|
+
const root = join(codexHome, "automations");
|
|
45
|
+
let entries;
|
|
46
|
+
try { entries = await readdir(root, { withFileTypes: true }); }
|
|
47
|
+
catch (error) { return { schedules, errors: error.code === "ENOENT" ? 0 : 1 }; }
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory()) continue;
|
|
50
|
+
try {
|
|
51
|
+
const config = parseToml(await readFile(join(root, entry.name, "automation.toml"), "utf8"));
|
|
52
|
+
if (config.kind !== "heartbeat" || typeof config.target_thread_id !== "string" || typeof config.id !== "string") continue;
|
|
53
|
+
const list = schedules.get(config.target_thread_id) ?? [];
|
|
54
|
+
list.push({ id: config.id, active: config.status === "ACTIVE" });
|
|
55
|
+
schedules.set(config.target_thread_id, list);
|
|
56
|
+
} catch (error) { if (error.code !== "ENOENT") errors += 1; }
|
|
57
|
+
}
|
|
58
|
+
// The desktop scheduler stores its actual/nominal next run here, outside TOML.
|
|
59
|
+
let runtime;
|
|
60
|
+
try {
|
|
61
|
+
const path = join(codexHome, "sqlite", "codex-dev.db");
|
|
62
|
+
await stat(path);
|
|
63
|
+
const { DatabaseSync } = await sqlite();
|
|
64
|
+
runtime = new DatabaseSync(path, { readOnly: true });
|
|
65
|
+
const fields = runtime.prepare("PRAGMA table_info(automations)").all().map(row => row.name);
|
|
66
|
+
if (fields.includes("next_run_at")) {
|
|
67
|
+
const nominal = fields.includes("next_run_nominal_at") ? "next_run_nominal_at" : "NULL";
|
|
68
|
+
for (const row of runtime.prepare(`SELECT id, target_thread_id, next_run_at, ${nominal} AS nominal FROM automations WHERE kind = 'heartbeat' AND status = 'ACTIVE'`).all()) {
|
|
69
|
+
const schedule = schedules.get(row.target_thread_id)?.find(schedule => schedule.id === row.id && schedule.active);
|
|
70
|
+
const ms = row.next_run_at == null ? null : row.nominal ?? row.next_run_at;
|
|
71
|
+
if (schedule && Number.isFinite(ms) && Number.isFinite(new Date(ms).getTime())) schedule.nextRunAt = iso(ms);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (error) { if (error.code !== "ENOENT") errors += 1; }
|
|
75
|
+
finally { runtime?.close(); }
|
|
76
|
+
return { schedules, errors };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Optional desktop queue. Keep attachment bytes out of board snapshots.
|
|
80
|
+
async function readPromptQueue(codexHome, imageCache) {
|
|
81
|
+
const activePaths = new Set();
|
|
82
|
+
const resolvedSources = new Map();
|
|
83
|
+
const prompts = new Map();
|
|
84
|
+
const images = new Map();
|
|
85
|
+
let database;
|
|
86
|
+
let errors = 0;
|
|
87
|
+
try {
|
|
88
|
+
const path = join(codexHome, "queue_1.sqlite");
|
|
89
|
+
await stat(path);
|
|
90
|
+
const { DatabaseSync } = await sqlite();
|
|
91
|
+
database = new DatabaseSync(path, { readOnly: true });
|
|
92
|
+
for (const row of database.prepare("SELECT id, thread_id, payload_json FROM queued_items ORDER BY queue_order, created_at_ms, id").all()) {
|
|
93
|
+
try {
|
|
94
|
+
const input = JSON.parse(row.payload_json)?.UserInput;
|
|
95
|
+
if (!Array.isArray(input?.content)) continue;
|
|
96
|
+
const rawText = input.content.filter(part => part.type === "text" && typeof part.text === "string").map(part => part.text).join("\n");
|
|
97
|
+
const text = displayUserPrompt(rawText);
|
|
98
|
+
const media = [];
|
|
99
|
+
for (const part of input.content) {
|
|
100
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
101
|
+
media.push(...replyImages(displayUserPrompt(part.text), text).map(image => ({ ...image, source: image.url })));
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (part.type !== "image" || typeof part.image_url !== "string") continue;
|
|
105
|
+
const data = part.image_url;
|
|
106
|
+
// Queue images are already embedded by Codex. Never fetch remote media.
|
|
107
|
+
if (data.length > 8 * 1024 * 1024 + 64 || !/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/=]+$/.test(data)) continue;
|
|
108
|
+
if (!resolvedSources.has(data)) {
|
|
109
|
+
const bytes = Buffer.from(data.slice(data.indexOf(",") + 1), "base64");
|
|
110
|
+
if (bytes.length > 6 * 1024 * 1024) continue;
|
|
111
|
+
resolvedSources.set(data, { source: data, identity: createHash("sha256").update(bytes).digest("hex") });
|
|
112
|
+
}
|
|
113
|
+
media.push({ alt: `Queued image ${media.length + 1}`, source: data });
|
|
114
|
+
}
|
|
115
|
+
// Codex can supply the same attachment as a local reference and embedded
|
|
116
|
+
// bytes. Compare validated image bytes, preserving first occurrence order.
|
|
117
|
+
const uniqueMedia = [];
|
|
118
|
+
const seenMedia = new Set();
|
|
119
|
+
for (const image of media) {
|
|
120
|
+
let resolved = resolvedSources.get(image.source);
|
|
121
|
+
if (!resolved) {
|
|
122
|
+
let source = image.source;
|
|
123
|
+
let fingerprint;
|
|
124
|
+
if (!source.startsWith("data:")) {
|
|
125
|
+
activePaths.add(source);
|
|
126
|
+
try {
|
|
127
|
+
const info = await stat(source).catch(error => {
|
|
128
|
+
if (error.code !== "ENOENT") throw error;
|
|
129
|
+
return stat(decodeURIComponent(source));
|
|
130
|
+
});
|
|
131
|
+
fingerprint = JSON.stringify([info.dev, info.ino, info.size, info.mtimeMs, info.ctimeMs]);
|
|
132
|
+
const cached = imageCache.get(source);
|
|
133
|
+
if (cached?.fingerprint === fingerprint) resolved = cached;
|
|
134
|
+
else source = await localReplyImage(source, { maxBytes: 6 * 1024 * 1024 }) ?? source;
|
|
135
|
+
} catch { imageCache.delete(source); /* Retry unavailable local images on a later scan. */ }
|
|
136
|
+
}
|
|
137
|
+
if (!resolved) {
|
|
138
|
+
const identity = createHash("sha256").update(source.startsWith("data:") ? Buffer.from(source.slice(source.indexOf(",") + 1), "base64") : source).digest("hex");
|
|
139
|
+
resolved = { source, identity };
|
|
140
|
+
if (fingerprint && source.startsWith("data:")) imageCache.set(image.source, { ...resolved, fingerprint });
|
|
141
|
+
}
|
|
142
|
+
resolvedSources.set(image.source, resolved);
|
|
143
|
+
}
|
|
144
|
+
const { source, identity } = resolved;
|
|
145
|
+
if (seenMedia.has(identity)) continue;
|
|
146
|
+
seenMedia.add(identity);
|
|
147
|
+
uniqueMedia.push({ ...image, source });
|
|
148
|
+
}
|
|
149
|
+
const promptImages = uniqueMedia.map((image, index) => {
|
|
150
|
+
const url = `taskchef-queue:${row.id}:${index}:${createHash("sha256").update(image.source).digest("hex").slice(0, 16)}`;
|
|
151
|
+
images.set(`${row.thread_id}:${url}`, image.source);
|
|
152
|
+
return { url, alt: image.alt };
|
|
153
|
+
});
|
|
154
|
+
const list = prompts.get(row.thread_id) ?? [];
|
|
155
|
+
list.push({ id: row.id, text, images: promptImages });
|
|
156
|
+
prompts.set(row.thread_id, list);
|
|
157
|
+
} catch { errors += 1; }
|
|
158
|
+
}
|
|
159
|
+
} catch (error) { if (error.code !== "ENOENT") errors += 1; }
|
|
160
|
+
finally {
|
|
161
|
+
database?.close();
|
|
162
|
+
for (const path of imageCache.keys()) if (!activePaths.has(path)) imageCache.delete(path);
|
|
163
|
+
}
|
|
164
|
+
return { prompts, images, errors };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function readDoneMarks(path) {
|
|
168
|
+
try {
|
|
169
|
+
const marks = JSON.parse(await readFile(path, "utf8"));
|
|
170
|
+
if (!marks || typeof marks !== "object" || Array.isArray(marks) || Object.values(marks).some((v) => typeof v !== "string")) throw new Error("Invalid TaskChef Done marks.");
|
|
171
|
+
return marks;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (error.code === "ENOENT") return {};
|
|
174
|
+
throw new DoneStateError("TaskChef: the local Done state file is unavailable or invalid. Repair that file before changing Done marks.");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Codex keeps a chat ID stable after revert, but selects a new rollout ID.
|
|
179
|
+
// Turn and item rows use the rollout ID encoded in the selected filename.
|
|
180
|
+
function historyId(row) {
|
|
181
|
+
const uuid = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
|
182
|
+
const name = basename(row.rollout_path || "");
|
|
183
|
+
const match = new RegExp(`^rollout-\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}-(${uuid})(?:_(${uuid}))?\\.jsonl(?:\\.zst)?$`).exec(name);
|
|
184
|
+
const timestamp = name.slice(8, 27).replace(/T(\d{2})-(\d{2})-(\d{2})$/, "T$1:$2:$3");
|
|
185
|
+
const date = new Date(`${timestamp}Z`);
|
|
186
|
+
const validTimestamp = Number.isFinite(date.getTime()) && date.toISOString().slice(0, 19) === timestamp;
|
|
187
|
+
if (match && validTimestamp) {
|
|
188
|
+
if (match[1].toLowerCase() !== row.id.toLowerCase()) throw new InvalidRolloutMetadataError("TaskChef: the selected rollout filename belongs to a different chat. Current turn lookup is unavailable.");
|
|
189
|
+
return (match[2] || match[1]).toLowerCase();
|
|
190
|
+
}
|
|
191
|
+
if (row.history_mode === "paginated") throw new InvalidRolloutMetadataError("TaskChef: a paginated chat has an invalid selected rollout filename. Current turn lookup is unavailable.");
|
|
192
|
+
// Codex explicitly supports noncanonical filenames for legacy histories.
|
|
193
|
+
return row.id;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function delegatedPrompt(item) {
|
|
197
|
+
if (item?.type !== "functionCallOutput" || item.namespace !== "codex_app" || item.name !== "create_thread" || typeof item.output !== "string") return null;
|
|
198
|
+
const match = /^\s*<codex_delegation>\s*<source_thread_id>[^<>]+<\/source_thread_id>\s*<input>([\s\S]*)<\/input>\s*<\/codex_delegation>\s*$/.exec(item.output);
|
|
199
|
+
return match ? match[1] : null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function unansweredQuestions(records) {
|
|
203
|
+
const questions = new Map();
|
|
204
|
+
const answers = new Set();
|
|
205
|
+
for (const record of records) {
|
|
206
|
+
let item;
|
|
207
|
+
try { item = JSON.parse(record.item_json); } catch { continue; }
|
|
208
|
+
if (item.type === "agentMessage" && typeof item.id === "string" && Array.isArray(item.questions)) {
|
|
209
|
+
item.questions.forEach((question, index) => {
|
|
210
|
+
if (typeof question?.title === "string" && question.title.trim()) {
|
|
211
|
+
const id = JSON.stringify(["request_user_input_async", item.id, index]);
|
|
212
|
+
questions.set(id, { id, title: question.title, options: Array.isArray(question.options) ? question.options.filter(option => typeof option === "string" && option.trim()) : [] });
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (item.type !== "userMessage" && !(item.type === "steeringUserMessage" && item.status === "accepted")) continue;
|
|
217
|
+
const content = item.type === "userMessage" ? item.content : item.input;
|
|
218
|
+
if (!Array.isArray(content) || content.length !== 1 || content[0]?.type !== "text") continue;
|
|
219
|
+
const match = /^\s*<send_user_message_question_reply>([\s\S]*)<\/send_user_message_question_reply>\s*$/.exec(content[0].text ?? "");
|
|
220
|
+
if (!match) continue;
|
|
221
|
+
try {
|
|
222
|
+
const value = JSON.parse(match[1]);
|
|
223
|
+
for (const answer of Array.isArray(value) ? value : [value]) {
|
|
224
|
+
if (typeof answer?.questionItemId === "string" && typeof answer.answer === "string") answers.add(answer.questionItemId);
|
|
225
|
+
}
|
|
226
|
+
} catch { /* An unrecognized reply cannot prove that the question was answered. */ }
|
|
227
|
+
}
|
|
228
|
+
return [...questions.values()].filter(question => !answers.has(question.id));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function readDatabaseRecords(state, history) {
|
|
232
|
+
const eligible = `coalesce(thread_source, '') NOT IN ('subagent', 'guardian_review')
|
|
233
|
+
AND CASE WHEN json_valid(source) THEN json_type(source, '$.subagent') IS NULL ELSE 1 END
|
|
234
|
+
AND NOT EXISTS (SELECT 1 FROM thread_spawn_edges WHERE child_thread_id = threads.id)`;
|
|
235
|
+
const latestTurn = history.prepare(`SELECT turn_id, status, started_at, duration_ms, first_user_item_id, final_agent_item_id
|
|
236
|
+
FROM thread_turns WHERE thread_id = ? ORDER BY rollout_ordinal DESC LIMIT 1`);
|
|
237
|
+
const turns = new Map();
|
|
238
|
+
const needsAnswers = new Map();
|
|
239
|
+
const rows = state.prepare(`SELECT id, name, title, cwd, archived, source, created_at_ms, updated_at_ms, recency_at_ms, rollout_path, history_mode, project_id,
|
|
240
|
+
(SELECT count(*) FROM thread_spawn_edges WHERE parent_thread_id = threads.id) AS child_count
|
|
241
|
+
FROM threads WHERE ${eligible} ORDER BY recency_at_ms DESC`).all().filter((row) => {
|
|
242
|
+
const key = historyId(row);
|
|
243
|
+
const turn = latestTurn.get(key);
|
|
244
|
+
if (!turn) return false;
|
|
245
|
+
turns.set(key, turn);
|
|
246
|
+
return true;
|
|
247
|
+
});
|
|
248
|
+
const input = history.prepare("SELECT item_json FROM thread_items WHERE thread_id = ? AND turn_id = ? AND item_id = ?");
|
|
249
|
+
const latestReply = history.prepare("SELECT item_json FROM thread_items WHERE thread_id = ? AND turn_id = ? AND item_type = 'agentMessage' ORDER BY rollout_ordinal DESC LIMIT 1");
|
|
250
|
+
const attachCalls = history.prepare("SELECT item_json FROM thread_items WHERE thread_id = ? AND turn_id = ? AND item_type = 'mcpToolCall'");
|
|
251
|
+
const delegationCalls = history.prepare("SELECT item_json FROM thread_items WHERE thread_id = ? AND turn_id = ? AND item_type = 'functionCallOutput' ORDER BY rollout_ordinal");
|
|
252
|
+
const questionItems = history.prepare(`SELECT item_json FROM thread_items WHERE thread_id = ? AND turn_id = ?
|
|
253
|
+
AND ((item_type = 'agentMessage' AND json_array_length(item_json, '$.questions') > 0)
|
|
254
|
+
OR (item_type IN ('userMessage', 'steeringUserMessage') AND instr(item_json, 'send_user_message_question_reply') > 0)) ORDER BY rollout_ordinal`);
|
|
255
|
+
const turnAttachments = new Map();
|
|
256
|
+
const inputs = new Map();
|
|
257
|
+
const replies = new Map();
|
|
258
|
+
for (const row of rows) {
|
|
259
|
+
const key = historyId(row);
|
|
260
|
+
const turn = turns.get(key);
|
|
261
|
+
needsAnswers.set(key, unansweredQuestions(questionItems.all(key, turn.turn_id)));
|
|
262
|
+
turnAttachments.set(key, attachCalls.all(key, turn.turn_id).map((row) => JSON.parse(row.item_json))
|
|
263
|
+
.filter((item) => item.server === "codex_app" && item.tool === "attach_artifact" && item.status === "completed" && !item.error && !item.result?.isError
|
|
264
|
+
&& item.arguments?.artifact_type === "pull_request" && typeof item.arguments.url === "string")
|
|
265
|
+
.map((item) => item.arguments.url));
|
|
266
|
+
let rawInput = turn.first_user_item_id ? input.get(key, turn.turn_id, turn.first_user_item_id)?.item_json : null;
|
|
267
|
+
if (!rawInput) rawInput = delegationCalls.all(key, turn.turn_id).find(record => delegatedPrompt(JSON.parse(record.item_json)) !== null)?.item_json;
|
|
268
|
+
inputs.set(key, rawInput ?? null);
|
|
269
|
+
replies.set(key, (turn.final_agent_item_id ? input.get(key, turn.turn_id, turn.final_agent_item_id) : latestReply.get(key, turn.turn_id))?.item_json);
|
|
270
|
+
}
|
|
271
|
+
const projects = state.prepare("SELECT id, name FROM projects ORDER BY position, id").all().map((project) => ({
|
|
272
|
+
...project, roots: state.prepare("SELECT path FROM project_roots WHERE project_id = ? ORDER BY position").all(project.id).map((root) => root.path),
|
|
273
|
+
}));
|
|
274
|
+
const attachments = new Map();
|
|
275
|
+
// Older Codex builds have no attachment registry. Never infer ownership from transcript links.
|
|
276
|
+
if (state.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'thread_attachments'").get()) {
|
|
277
|
+
for (const row of state.prepare("SELECT thread_id, payload FROM thread_attachments WHERE attachment_type = 'pull_request'").all()) {
|
|
278
|
+
const payload = JSON.parse(row.payload);
|
|
279
|
+
if (typeof payload.url !== "string") throw new Error("Invalid PR attachment.");
|
|
280
|
+
const urls = attachments.get(row.thread_id) ?? [];
|
|
281
|
+
if (!urls.includes(payload.url)) urls.push(payload.url);
|
|
282
|
+
attachments.set(row.thread_id, urls);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return { rows, turns, inputs, replies, projects, attachments, turnAttachments, needsAnswers };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function buildDatabaseSnapshot(records, now, doneMarks, schedules, scheduleErrors, cacheHit) {
|
|
289
|
+
const { rows, turns, inputs, replies } = records;
|
|
290
|
+
const images = records.images ??= new Map();
|
|
291
|
+
const content = records.latestContent ??= new Map();
|
|
292
|
+
const tasks = rows.map((row) => {
|
|
293
|
+
const turn = turns.get(historyId(row));
|
|
294
|
+
const updatedMs = Math.max(row.updated_at_ms || 0, row.recency_at_ms || 0, row.created_at_ms || 0, (turn.started_at || 0) * 1000);
|
|
295
|
+
const active = !row.archived && turn.status === "inProgress";
|
|
296
|
+
const chatSchedules = schedules.get(row.id) ?? [];
|
|
297
|
+
const scheduled = chatSchedules.some((schedule) => schedule.active);
|
|
298
|
+
const nextRunAt = chatSchedules.filter(schedule => schedule.active && schedule.nextRunAt).map(schedule => schedule.nextRunAt).sort()[0] ?? null;
|
|
299
|
+
let inputSource = "unverified";
|
|
300
|
+
let inputText = "";
|
|
301
|
+
{
|
|
302
|
+
const raw = inputs.get(historyId(row));
|
|
303
|
+
if (raw) {
|
|
304
|
+
const item = JSON.parse(raw);
|
|
305
|
+
const delegated = delegatedPrompt(item);
|
|
306
|
+
const text = inputText = delegated ?? (item.content ?? []).filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
307
|
+
const marker = /^\s*<heartbeat>\s*<automation_id>([^<]+)<\/automation_id>/.exec(text);
|
|
308
|
+
const inputSchedule = marker && !item.clientId && delegated === null;
|
|
309
|
+
if (inputSchedule) { inputSource = "scheduled"; }
|
|
310
|
+
else if (delegated !== null || (!marker && item.clientId)) inputSource = "ordinary";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const rawReply = replies.get(historyId(row));
|
|
314
|
+
const reply = rawReply ? JSON.parse(rawReply) : null;
|
|
315
|
+
let replyText = reply?.type === "agentMessage" && typeof reply.text === "string" ? reply.text.trim() : "";
|
|
316
|
+
// Heartbeat replies wrap their user-facing text in a message block.
|
|
317
|
+
if (/^<heartbeat>[\s\S]*<\/heartbeat>$/.test(replyText)) {
|
|
318
|
+
replyText = /<message>([\s\S]*?)<\/message>/.exec(replyText)?.[1].trim() || "";
|
|
319
|
+
}
|
|
320
|
+
const promptText = inputSource === "scheduled"
|
|
321
|
+
? (/<instructions>([\s\S]*?)<\/instructions>/.exec(inputText)?.[1].trim() || "") : displayUserPrompt(inputText);
|
|
322
|
+
const promptExcerpt = promptText.slice(0, 2000) || null;
|
|
323
|
+
const replyExcerpt = replyText.slice(0, 2000) || null;
|
|
324
|
+
const promptImages = replyImages(promptText);
|
|
325
|
+
const annotations = responseAnnotations(inputText);
|
|
326
|
+
content.set(row.id, { promptText: promptText || null, replyText: replyText || null, promptImages });
|
|
327
|
+
if (!images.has(row.id)) images.set(row.id, replyImage(replyText));
|
|
328
|
+
// Ownership comes from the registry; latest-turn references only narrow that owned set.
|
|
329
|
+
const references = new Set([...`${inputText}\n${replyText}`.matchAll(/https:\/\/github\.com\/[A-Za-z0-9-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9]\d*\/?(?![A-Za-z0-9_/-])/g)].map((match) => match[0].replace(/\/$/, "")));
|
|
330
|
+
for (const url of records.turnAttachments.get(historyId(row)) ?? []) references.add(url.replace(/\/$/, ""));
|
|
331
|
+
const turnPrs = (records.attachments.get(row.id) ?? []).filter((url) => references.has(url.replace(/\/$/, "")));
|
|
332
|
+
const manualDone = !active && !scheduled && doneMarks[row.id] === turn.turn_id && turnPrs.every(url => (doneMarks[`done-pr:${row.id}`] ?? "").split("\n").includes(url));
|
|
333
|
+
const manualScheduled = scheduled && !active && doneMarks[`scheduled:${row.id}`] === turn.turn_id;
|
|
334
|
+
const status = row.archived ? "archived" : manualDone ? "completed" : manualScheduled ? "scheduled" : active ? "working"
|
|
335
|
+
: ["failed", "interrupted"].includes(turn.status) ? "interrupted"
|
|
336
|
+
: turn.status === "completed" ? scheduled && inputSource === "scheduled" ? "scheduled" : "needs_input" : null;
|
|
337
|
+
const statusLabel = status === "archived" ? "Archived" : status === "completed" ? "Done" : status === "working" ? "Running" : status === "needs_input" ? "Waiting for input/review" : status === "scheduled" ? "Scheduled" : status === "interrupted" ? "Interrupted" : "Unverified";
|
|
338
|
+
const reason = row.archived ? "Chat is archived." : manualDone ? "Marked Done in TaskChef. A new turn resets this mark."
|
|
339
|
+
: active ? "Latest selected turn is in progress."
|
|
340
|
+
: status === "scheduled" ? manualScheduled ? "Latest human turn acknowledged in TaskChef; an active schedule remains." : "Latest turn was a scheduled heartbeat; an active schedule remains."
|
|
341
|
+
: status === "needs_input" ? "Latest turn ended. Chat remains open for input or review."
|
|
342
|
+
: status === "interrupted" ? `Latest turn is ${turn.status}.` : "Latest turn state is unrecognized.";
|
|
343
|
+
const cwd = row.cwd || "";
|
|
344
|
+
return {
|
|
345
|
+
id: row.id, title: row.name?.trim() || row.title?.trim() || `Codex chat ${row.id.slice(0, 8)}`,
|
|
346
|
+
instruction: "Chat name and title are local metadata and can contain user text.",
|
|
347
|
+
summary: reason, promptExcerpt, replyExcerpt, annotations, promptImages, promptImage: promptImages[0] ?? null, replyImage: images.get(row.id),
|
|
348
|
+
needsAnswer: !row.archived && (records.needsAnswers.get(historyId(row))?.length ?? 0) > 0,
|
|
349
|
+
pendingQuestions: row.archived ? [] : records.needsAnswers.get(historyId(row)) ?? [],
|
|
350
|
+
status, statusLabel, scheduled, nextRunAt, manualDone, manualScheduled, inputSource,
|
|
351
|
+
pullRequests: turnPrs.map((url) => ({ url, state: "unknown", checks: "unknown" })),
|
|
352
|
+
createdAt: iso(row.created_at_ms || updatedMs), updatedAt: iso(updatedMs),
|
|
353
|
+
updatedBy: "Local Codex database", project: { id: row.project_id, name: basename(cwd) || cwd || "Unknown project", path: cwd, githubRepos: [] },
|
|
354
|
+
threadId: row.id, turnRef: turn.turn_id, turnId: turn.turn_id, lastResult: null, latestTurn: null,
|
|
355
|
+
observed: { source: row.source, archive: Boolean(row.archived), lastTurnEvent: turn.status, lastTurnEventAt: turn.started_at ? iso(turn.started_at * 1000) : null, latestTurnDurationMs: turn.status !== "inProgress" && Number.isFinite(turn.duration_ms) && turn.duration_ms >= 0 ? turn.duration_ms : null, recentFileActivity: !row.archived && now - updatedMs < ACTIVE_WINDOW_MS, directChildCount: row.child_count },
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
return { tasks, rolloutPaths: new Map(rows.map((row) => [row.id, row.rollout_path])), scan: {
|
|
359
|
+
source: "database", mode: "database", cacheHit, checkedAt: iso(now), intervalSeconds: 5,
|
|
360
|
+
visibleFiles: tasks.length, indexedFiles: tasks.length, unreadFiles: 0,
|
|
361
|
+
activeFiles: null, archivedFiles: null, parsedFiles: null, errors: 0, scheduleErrors,
|
|
362
|
+
sources: ["state_5.sqlite:threads", "state_5.sqlite:thread_spawn_edges", "thread_history_1.sqlite:thread_turns", "thread_history_1.sqlite:thread_items", "automations/*/automation.toml"],
|
|
363
|
+
fields: ["session ID", "name", "title", "timestamps", "project directory", "archive flag", "thread source", "direct child count", "latest turn status", "heartbeat marker", "schedule flag"],
|
|
364
|
+
} };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function parseLines(text, onRecord) {
|
|
368
|
+
const lines = text.split("\n");
|
|
369
|
+
for (const [index, line] of lines.entries()) {
|
|
370
|
+
if (!line) continue;
|
|
371
|
+
try { onRecord(JSON.parse(line)); }
|
|
372
|
+
catch (error) {
|
|
373
|
+
if (index === lines.length - 1 && !text.endsWith("\n")) continue; // A live append can leave a partial line.
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function readSlice(handle, start, bytes) {
|
|
380
|
+
if (bytes <= 0) return "";
|
|
381
|
+
const buffer = Buffer.allocUnsafe(bytes);
|
|
382
|
+
const { bytesRead } = await handle.read(buffer, 0, bytes, start);
|
|
383
|
+
return buffer.subarray(0, bytesRead).toString("utf8");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function readSession(file, now) {
|
|
387
|
+
const handle = await open(file.path, "r");
|
|
388
|
+
try {
|
|
389
|
+
const size = (await handle.stat()).size;
|
|
390
|
+
let headEnd = Math.min(size, HEAD_BYTES);
|
|
391
|
+
let head = await readSlice(handle, 0, headEnd);
|
|
392
|
+
if (!head.includes("\n") && headEnd < size) {
|
|
393
|
+
headEnd = Math.min(size, META_LINE_BYTES);
|
|
394
|
+
head = await readSlice(handle, 0, headEnd);
|
|
395
|
+
}
|
|
396
|
+
const tailStart = Math.max(0, size - TAIL_BYTES);
|
|
397
|
+
const contiguous = tailStart <= headEnd;
|
|
398
|
+
const tail = contiguous ? await readSlice(handle, headEnd, size - headEnd) : await readSlice(handle, tailStart, size - tailStart);
|
|
399
|
+
let cwd = "";
|
|
400
|
+
let createdAt = "";
|
|
401
|
+
let lastEvent = "";
|
|
402
|
+
let lastEventAt = "";
|
|
403
|
+
let userMessages = 0;
|
|
404
|
+
let assistantMessages = 0;
|
|
405
|
+
let hasSessionMeta = false;
|
|
406
|
+
const accept = (record) => {
|
|
407
|
+
const payload = record?.payload;
|
|
408
|
+
if (record?.type === "session_meta") {
|
|
409
|
+
if (payload?.id === file.id && typeof payload.cwd === "string" && payload.cwd) {
|
|
410
|
+
hasSessionMeta = true;
|
|
411
|
+
cwd = payload.cwd;
|
|
412
|
+
createdAt = typeof payload.timestamp === "string" ? payload.timestamp : createdAt;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (record?.type === "event_msg" && ["task_started", "task_complete", "turn_aborted"].includes(payload?.type)) {
|
|
416
|
+
lastEvent = payload.type;
|
|
417
|
+
lastEventAt = typeof record.timestamp === "string" ? record.timestamp : lastEventAt;
|
|
418
|
+
}
|
|
419
|
+
if (record?.type === "response_item" && payload?.type === "message") {
|
|
420
|
+
if (payload.role === "user") userMessages += 1;
|
|
421
|
+
if (payload.role === "assistant") assistantMessages += 1;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
if (contiguous) parseLines(head + tail, accept);
|
|
425
|
+
else {
|
|
426
|
+
parseLines(head.slice(0, head.lastIndexOf("\n") + 1), accept);
|
|
427
|
+
// A turn event in the unsampled middle could supersede the head event.
|
|
428
|
+
lastEvent = "";
|
|
429
|
+
lastEventAt = "";
|
|
430
|
+
parseLines(tail.slice(tail.indexOf("\n") + 1), accept);
|
|
431
|
+
}
|
|
432
|
+
if (!hasSessionMeta) throw new Error("Session metadata is missing or invalid.");
|
|
433
|
+
const updatedAt = new Date(file.mtimeMs).toISOString();
|
|
434
|
+
const projectName = cwd ? basename(cwd) || cwd : "Unknown project";
|
|
435
|
+
const active = !file.archive && lastEvent === "task_started" && now - file.mtimeMs < ACTIVE_WINDOW_MS;
|
|
436
|
+
const activity = active ? "A turn started recently; live activity is unverified."
|
|
437
|
+
: lastEvent === "task_complete" ? "Latest recorded turn ended; task outcome is unknown."
|
|
438
|
+
: lastEvent === "turn_aborted" ? "Latest recorded turn stopped; task outcome is unknown."
|
|
439
|
+
: "Task outcome is unknown from the available log segment.";
|
|
440
|
+
return {
|
|
441
|
+
id: file.id, title: `Codex chat ${file.id.slice(0, 8)}`, instruction: "Private request text is not shown in this read-only demo.",
|
|
442
|
+
summary: activity, status: active ? "working" : null, createdAt: createdAt || updatedAt, updatedAt,
|
|
443
|
+
updatedBy: "Local Codex log", project: { name: projectName, path: cwd, githubRepos: [] },
|
|
444
|
+
threadId: file.id, turnRef: null, turnId: null, lastResult: null, latestTurn: null,
|
|
445
|
+
observed: { archive: file.archive, lastTurnEvent: lastEvent || null, lastTurnEventAt: lastEventAt || null, recentFileActivity: !file.archive && now - file.mtimeMs < ACTIVE_WINDOW_MS, userMessages, assistantMessages, sampledBytes: Math.min(size, headEnd + TAIL_BYTES), fileBytes: size },
|
|
446
|
+
};
|
|
447
|
+
} finally { await handle.close(); }
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export class CodexSessionScanner {
|
|
451
|
+
constructor({ codexHome = process.env.CODEX_HOME || join(homedir(), ".codex"), now = () => Date.now(), statePath = join(homedir(), ".agents", "taskchef-next", "done.json"), nodeVersion = process.versions.node } = {}) {
|
|
452
|
+
this.codexHome = codexHome;
|
|
453
|
+
this.now = now;
|
|
454
|
+
this.projectCatalog = new CodexProjects(codexHome);
|
|
455
|
+
this.projects = [];
|
|
456
|
+
this.nodeVersion = nodeVersion;
|
|
457
|
+
this.statePath = statePath;
|
|
458
|
+
this.sessionUsage = new CodexSessionUsage({ codexHome, now, cacheDir: join(dirname(statePath), "cache", "usage"), legacyCacheDir: join(dirname(statePath), "usage-cache"),
|
|
459
|
+
pricing: new CodexPricing({ cachePath: join(dirname(statePath), "cache", "pricing.json"), legacyCachePath: join(dirname(statePath), "pricing.json"), now }) });
|
|
460
|
+
this.mutation = Promise.resolve();
|
|
461
|
+
this.tasks = new Map();
|
|
462
|
+
this.rolloutPaths = new Map();
|
|
463
|
+
this.revision = 0;
|
|
464
|
+
this.stats = null;
|
|
465
|
+
this.pending = null;
|
|
466
|
+
this.forcedPending = null;
|
|
467
|
+
this.connections = null;
|
|
468
|
+
this.databaseCache = null;
|
|
469
|
+
this.doneCache = null;
|
|
470
|
+
}
|
|
471
|
+
async refresh({ force = false } = {}) {
|
|
472
|
+
if (this.pending) {
|
|
473
|
+
if (!force) return this.pending;
|
|
474
|
+
if (!this.forcedPending) {
|
|
475
|
+
this.forcedPending = this.pending.then(() => {
|
|
476
|
+
this.forcedPending = null;
|
|
477
|
+
return this.refresh({ force: true });
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
return this.forcedPending;
|
|
481
|
+
}
|
|
482
|
+
this.pending = this.refreshSource(this.now(), force).finally(() => { this.pending = null; });
|
|
483
|
+
return this.pending;
|
|
484
|
+
}
|
|
485
|
+
async doneMarks(force) {
|
|
486
|
+
let stamp;
|
|
487
|
+
try {
|
|
488
|
+
const info = await stat(this.statePath, { bigint: true });
|
|
489
|
+
stamp = `${info.dev}:${info.ino}:${info.mtimeNs}:${info.ctimeNs}:${info.size}`;
|
|
490
|
+
} catch (error) {
|
|
491
|
+
if (error.code !== "ENOENT") throw new DoneStateError("TaskChef: the local Done state file is unavailable or invalid. Repair that file before changing Done marks.");
|
|
492
|
+
stamp = "missing";
|
|
493
|
+
}
|
|
494
|
+
if (!force && this.doneCache?.stamp === stamp) return this.doneCache.marks;
|
|
495
|
+
// Capture the stamp before reading. A concurrent rewrite is checked again
|
|
496
|
+
// on the next poll; mutation paths always read the file under their lock.
|
|
497
|
+
const marks = stamp === "missing" ? {} : await readDoneMarks(this.statePath);
|
|
498
|
+
this.doneCache = { stamp, marks };
|
|
499
|
+
return marks;
|
|
500
|
+
}
|
|
501
|
+
async openDatabases() {
|
|
502
|
+
if (!supportsReadOnlySqlite(this.nodeVersion)) throw new UnsupportedNodeVersionError(`TaskChef requires Node.js 22.18+, 23.2+, or 24+ for read-only SQLite (current: ${this.nodeVersion}).`);
|
|
503
|
+
const paths = [join(this.codexHome, "state_5.sqlite"), join(this.codexHome, "thread_history_1.sqlite")];
|
|
504
|
+
const identities = await Promise.all(paths.map(async (path) => {
|
|
505
|
+
const info = await stat(path, { bigint: true });
|
|
506
|
+
return `${info.dev}:${info.ino}`;
|
|
507
|
+
}));
|
|
508
|
+
if (!this.connections || JSON.stringify(identities) !== JSON.stringify(this.connections.identities)) {
|
|
509
|
+
this.close();
|
|
510
|
+
const module = await sqlite();
|
|
511
|
+
let state;
|
|
512
|
+
try {
|
|
513
|
+
state = new module.DatabaseSync(paths[0], { readOnly: true });
|
|
514
|
+
const history = new module.DatabaseSync(paths[1], { readOnly: true });
|
|
515
|
+
this.connections = { state, history, identities };
|
|
516
|
+
} catch (error) { state?.close(); throw error; }
|
|
517
|
+
}
|
|
518
|
+
return this.connections;
|
|
519
|
+
}
|
|
520
|
+
async databaseRecords(force) {
|
|
521
|
+
const { state, history } = await this.openDatabases();
|
|
522
|
+
// Compare versions only on these same connections. Capture before querying:
|
|
523
|
+
// a commit during a snapshot must trigger another read on the next poll.
|
|
524
|
+
const versions = [state.prepare("PRAGMA data_version").get().data_version, history.prepare("PRAGMA data_version").get().data_version];
|
|
525
|
+
const cacheHit = !force && this.databaseCache && JSON.stringify(versions) === JSON.stringify(this.databaseCache.versions);
|
|
526
|
+
if (!cacheHit) this.databaseCache = { versions, records: readDatabaseRecords(state, history) };
|
|
527
|
+
return { records: this.databaseCache.records, cacheHit: Boolean(cacheHit) };
|
|
528
|
+
}
|
|
529
|
+
async refreshSource(now, force) {
|
|
530
|
+
try {
|
|
531
|
+
if (!supportsReadOnlySqlite(this.nodeVersion)) throw new UnsupportedNodeVersionError(`TaskChef requires Node.js 22.18+, 23.2+, or 24+ for read-only SQLite (current: ${this.nodeVersion}).`);
|
|
532
|
+
const { records, cacheHit } = await this.databaseRecords(force);
|
|
533
|
+
const doneMarks = await this.doneMarks(force);
|
|
534
|
+
const { schedules, errors: scheduleErrors } = await readSchedules(this.codexHome);
|
|
535
|
+
const database = buildDatabaseSnapshot(records, now, doneMarks, schedules, scheduleErrors, cacheHit);
|
|
536
|
+
const queue = await readPromptQueue(this.codexHome, this.queueImageCache ??= new Map());
|
|
537
|
+
this.queuedImages = queue.images;
|
|
538
|
+
for (const task of database.tasks) task.queuedPrompts = queue.prompts.get(task.id) ?? [];
|
|
539
|
+
database.scan.queueErrors = queue.errors;
|
|
540
|
+
let grouped;
|
|
541
|
+
try { grouped = await this.projectCatalog.group(database.tasks, records.projects); }
|
|
542
|
+
catch { throw new ProjectMetadataError("TaskChef cannot read Codex project metadata. Check .codex-global-state.json and the project worktree metadata, then refresh."); }
|
|
543
|
+
const nextTasks = new Map(grouped.tasks.map((task) => [task.id, task]));
|
|
544
|
+
const signature = (tasks) => JSON.stringify([...tasks.values()].map((task) => [
|
|
545
|
+
task.id, task.title, task.project.path, task.project.id, task.project.name, task.updatedAt, task.status, task.summary,
|
|
546
|
+
task.observed.archive, task.observed.unread, task.observed.lastTurnEvent, task.observed.directChildCount, task.observed.latestTurnDurationMs,
|
|
547
|
+
task.scheduled, task.nextRunAt, task.inputSource, task.turnId, task.promptExcerpt, task.replyExcerpt, task.annotations, task.promptImages, task.promptImage, task.replyImage, task.queuedPrompts, task.needsAnswer, task.pendingQuestions, task.pullRequests,
|
|
548
|
+
]));
|
|
549
|
+
if (this.stats?.mode !== "database" || signature(this.tasks) !== signature(nextTasks) || JSON.stringify(this.projects) !== JSON.stringify(grouped.projects)) this.revision += 1;
|
|
550
|
+
this.tasks = nextTasks;
|
|
551
|
+
this.projects = grouped.projects;
|
|
552
|
+
this.rolloutPaths = database.rolloutPaths;
|
|
553
|
+
this.stats = database.scan;
|
|
554
|
+
return this.snapshot();
|
|
555
|
+
} catch (error) {
|
|
556
|
+
this.close();
|
|
557
|
+
if (this.stats?.mode !== "error" || this.tasks.size) this.revision += 1;
|
|
558
|
+
this.tasks = new Map();
|
|
559
|
+
this.projects = [];
|
|
560
|
+
this.rolloutPaths = new Map();
|
|
561
|
+
this.stats = { source: "database", mode: "error", checkedAt: iso(now), error: error instanceof UnsupportedNodeVersionError || error instanceof DoneStateError || error instanceof InvalidRolloutMetadataError || error instanceof ProjectMetadataError ? error.message : "Codex databases are unavailable or incompatible." };
|
|
562
|
+
return this.snapshot();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
snapshot() {
|
|
566
|
+
return { healthy: this.stats?.mode !== "error", revision: this.revision, tasks: [...this.tasks.values()], projects: this.projects, scan: this.stats };
|
|
567
|
+
}
|
|
568
|
+
task(id) { return this.tasks.get(id); }
|
|
569
|
+
earlierPullRequest(id) {
|
|
570
|
+
const task = this.task(id);
|
|
571
|
+
if (!task || task.pullRequests?.length || !this.databaseCache?.records) return null;
|
|
572
|
+
const records = this.databaseCache.records;
|
|
573
|
+
const cache = records.earlierPullRequests ??= new Map();
|
|
574
|
+
if (cache.has(id)) return cache.get(id);
|
|
575
|
+
const row = records.rows.find(row => row.id === id);
|
|
576
|
+
const owned = new Set((records.attachments.get(id) ?? []).map(url => url.replace(/\/$/, "")));
|
|
577
|
+
let pr = null;
|
|
578
|
+
if (row && owned.size) {
|
|
579
|
+
// Read only this selected rollout, newest first, and stop at the first owned PR.
|
|
580
|
+
const items = this.connections.history.prepare(`SELECT item_json FROM thread_items
|
|
581
|
+
WHERE thread_id = ? AND turn_id != ? AND item_type IN ('userMessage', 'agentMessage', 'mcpToolCall')
|
|
582
|
+
ORDER BY rollout_ordinal DESC`).iterate(historyId(row), task.turnId);
|
|
583
|
+
for (const record of items) {
|
|
584
|
+
let item;
|
|
585
|
+
try { item = JSON.parse(record.item_json); } catch { continue; }
|
|
586
|
+
if (!item || typeof item !== "object") continue;
|
|
587
|
+
const text = item.type === "agentMessage" ? item.text
|
|
588
|
+
: item.type === "userMessage" ? (item.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\n")
|
|
589
|
+
: item.server === "codex_app" && item.tool === "attach_artifact" && item.status === "completed" && !item.error && !item.result?.isError && item.arguments?.artifact_type === "pull_request" ? item.arguments.url : "";
|
|
590
|
+
if (typeof text !== "string") continue;
|
|
591
|
+
const url = [...text.matchAll(/https:\/\/github\.com\/[A-Za-z0-9-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9]\d*\/?(?![A-Za-z0-9_/-])/g)].reverse()
|
|
592
|
+
.map(match => match[0].replace(/\/$/, "")).find(url => owned.has(url));
|
|
593
|
+
if (url) { pr = { url, state: "unknown", checks: "unknown" }; break; }
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
cache.set(id, pr);
|
|
597
|
+
return pr;
|
|
598
|
+
}
|
|
599
|
+
async taskImage(id, expectedTurnId, expectedUrl) {
|
|
600
|
+
const snapshot = await this.refresh();
|
|
601
|
+
if (!snapshot.healthy) throw new Error(snapshot.scan.error);
|
|
602
|
+
const task = this.task(id);
|
|
603
|
+
if (!task || task.turnId !== expectedTurnId) return null;
|
|
604
|
+
const queuedImage = this.queuedImages?.get(`${id}:${expectedUrl}`);
|
|
605
|
+
if (queuedImage) {
|
|
606
|
+
if (queuedImage.startsWith("data:")) return queuedImage;
|
|
607
|
+
try { return await localReplyImage(queuedImage); } catch { return null; }
|
|
608
|
+
}
|
|
609
|
+
const content = this.databaseCache.records.latestContent.get(id);
|
|
610
|
+
const images = [...(content?.promptImages ?? []), ...replyImages(content?.replyText ?? "")];
|
|
611
|
+
const image = images.find(image => image.url === expectedUrl || normalizeUri(image.url) === expectedUrl);
|
|
612
|
+
if (!image) return null;
|
|
613
|
+
try { return await localReplyImage(image.url); } catch { return null; }
|
|
614
|
+
}
|
|
615
|
+
async taskUsage(id, savedDurations = []) {
|
|
616
|
+
const task = this.task(id);
|
|
617
|
+
if (!task) return null;
|
|
618
|
+
const sessions = this.connections.state.prepare(`WITH RECURSIVE family(id) AS (
|
|
619
|
+
SELECT ? UNION SELECT child_thread_id FROM thread_spawn_edges JOIN family ON parent_thread_id = family.id
|
|
620
|
+
) SELECT id, rollout_path FROM threads WHERE id IN (SELECT id FROM family)`).all(id);
|
|
621
|
+
return this.sessionUsage.read({ id, latestTurnId: task.turnId, sessions, savedDurations });
|
|
622
|
+
}
|
|
623
|
+
recordedUsage(id, turnId) {
|
|
624
|
+
const result = (this.recordedPending ?? Promise.resolve()).then(() => this.readRecordedUsage(id, turnId));
|
|
625
|
+
this.recordedPending = result.catch(() => {});
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
async readRecordedUsage(id, turnId) {
|
|
629
|
+
// Dispatcher chats can be exec/subagent sessions that the board intentionally hides.
|
|
630
|
+
// Read their linked family directly, without changing the board's inclusion rules.
|
|
631
|
+
await this.openDatabases();
|
|
632
|
+
const row = this.connections.state.prepare("SELECT id, rollout_path, history_mode FROM threads WHERE id = ?").get(id);
|
|
633
|
+
if (!row) throw new Error("No matching Codex usage session was found.");
|
|
634
|
+
const sessions = this.connections.state.prepare(`WITH RECURSIVE family(id) AS (
|
|
635
|
+
SELECT ? UNION SELECT child_thread_id FROM thread_spawn_edges JOIN family ON parent_thread_id = family.id
|
|
636
|
+
) SELECT id, rollout_path FROM threads WHERE id IN (SELECT id FROM family)`).all(id);
|
|
637
|
+
const matched = !!turnId && !!this.connections.history.prepare("SELECT 1 FROM thread_turns WHERE thread_id = ? AND turn_id = ?").get(historyId(row), turnId);
|
|
638
|
+
const usage = await this.sessionUsage.read({ id, latestTurnId: matched ? turnId : null, sessions });
|
|
639
|
+
return { ...usage, matchedTurn: matched };
|
|
640
|
+
}
|
|
641
|
+
async taskDetail(id) {
|
|
642
|
+
const task = this.task(id);
|
|
643
|
+
if (!task) return null;
|
|
644
|
+
// Details reads the full owned PR history of the selected rollout.
|
|
645
|
+
// Restrict links to owned PRs; incidental transcript links are not attachments.
|
|
646
|
+
const records = this.databaseCache.records;
|
|
647
|
+
const row = records.rows.find(row => row.id === id);
|
|
648
|
+
const owned = new Set((records.attachments.get(id) ?? []).map(url => url.replace(/\/$/, "")));
|
|
649
|
+
const cachedDetails = records.detailPullRequests ??= new Map();
|
|
650
|
+
let detailPullRequests = cachedDetails.get(id);
|
|
651
|
+
if (!detailPullRequests) {
|
|
652
|
+
const urls = new Set();
|
|
653
|
+
const items = owned.size ? this.connections.history.prepare(`SELECT item_json FROM thread_items
|
|
654
|
+
WHERE thread_id = ? AND item_type IN ('userMessage', 'agentMessage', 'mcpToolCall')
|
|
655
|
+
ORDER BY rollout_ordinal DESC`).iterate(historyId(row)) : [];
|
|
656
|
+
for (const record of items) {
|
|
657
|
+
let item;
|
|
658
|
+
try { item = JSON.parse(record.item_json); } catch { continue; }
|
|
659
|
+
if (!item || typeof item !== "object") continue;
|
|
660
|
+
const text = item.type === "agentMessage" ? item.text
|
|
661
|
+
: item.type === "userMessage" ? (item.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\n")
|
|
662
|
+
: item.server === "codex_app" && item.tool === "attach_artifact" && item.status === "completed" && !item.error && !item.result?.isError && item.arguments?.artifact_type === "pull_request" ? item.arguments.url : "";
|
|
663
|
+
if (typeof text !== "string") continue;
|
|
664
|
+
for (const match of [...text.matchAll(/https:\/\/github\.com\/[A-Za-z0-9-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9]\d*\/?(?![A-Za-z0-9_/-])/g)].reverse()) {
|
|
665
|
+
const url = match[0].replace(/\/$/, "");
|
|
666
|
+
if (owned.has(url)) urls.add(url);
|
|
667
|
+
}
|
|
668
|
+
if (urls.size === owned.size) break;
|
|
669
|
+
}
|
|
670
|
+
detailPullRequests = [...urls].map(url => ({ url, state: "unknown", checks: "unknown" }));
|
|
671
|
+
cachedDetails.set(id, detailPullRequests);
|
|
672
|
+
}
|
|
673
|
+
const durationCache = records.detailDurations ??= new Map();
|
|
674
|
+
let durations = durationCache.get(id);
|
|
675
|
+
if (!durations) {
|
|
676
|
+
const saved = this.connections.history.prepare(`SELECT turn_id, duration_ms
|
|
677
|
+
FROM thread_turns WHERE thread_id = ? AND status != 'inProgress'`).all(historyId(row));
|
|
678
|
+
durations = { saved, total: saved.reduce((sum, turn) => sum + (turn.duration_ms >= 0 ? turn.duration_ms ?? 0 : 0), 0),
|
|
679
|
+
missing: saved.filter(turn => turn.duration_ms == null || turn.duration_ms < 0).length };
|
|
680
|
+
durationCache.set(id, durations);
|
|
681
|
+
}
|
|
682
|
+
let detail = { ...task, ...records.latestContent.get(id), detailPullRequests, observed: { ...task.observed,
|
|
683
|
+
recordedChatDurationMs: durations.total ?? 0, missingTurnDurations: durations.missing ?? 0 } };
|
|
684
|
+
try {
|
|
685
|
+
const sessionUsage = await this.taskUsage(id, durations.saved);
|
|
686
|
+
detail = { ...detail, sessionUsage, observed: { ...detail.observed,
|
|
687
|
+
...(sessionUsage.durationTurns ? { recordedChatDurationMs: Math.max(durations.total ?? 0, sessionUsage.durationMs) } : {}),
|
|
688
|
+
historicalTimePartial: sessionUsage.partial || !sessionUsage.durationTurns } };
|
|
689
|
+
} catch { detail.observed.historicalTimePartial = true; }
|
|
690
|
+
try {
|
|
691
|
+
const path = this.rolloutPaths.get(id);
|
|
692
|
+
if (typeof path !== "string" || !isAbsolute(path)) return detail;
|
|
693
|
+
const info = await stat(path);
|
|
694
|
+
if (!info.isFile()) return detail;
|
|
695
|
+
const log = await readSession({ path, id, archive: task.observed.archive, mtimeMs: info.mtimeMs }, this.now());
|
|
696
|
+
return { ...detail, observed: { ...detail.observed,
|
|
697
|
+
userMessages: log.observed.userMessages,
|
|
698
|
+
assistantMessages: log.observed.assistantMessages,
|
|
699
|
+
sampledBytes: log.observed.sampledBytes,
|
|
700
|
+
fileBytes: log.observed.fileBytes,
|
|
701
|
+
} };
|
|
702
|
+
} catch { return detail; } // Optional log detail never changes database status or availability.
|
|
703
|
+
}
|
|
704
|
+
async setDone(id, expectedTurnId, done, mergedPrUrls = []) {
|
|
705
|
+
return this.setQueueMark(id, expectedTurnId, done, false, mergedPrUrls);
|
|
706
|
+
}
|
|
707
|
+
async setScheduled(id, expectedTurnId) {
|
|
708
|
+
return this.setQueueMark(id, expectedTurnId, true, true);
|
|
709
|
+
}
|
|
710
|
+
async setQueueMark(id, expectedTurnId, done, scheduled, mergedPrUrls = []) {
|
|
711
|
+
const action = this.mutation.then(async () => {
|
|
712
|
+
const snapshot = await this.refresh({ force: true });
|
|
713
|
+
if (!snapshot.healthy) throw new Error(snapshot.scan.error);
|
|
714
|
+
const task = this.task(id);
|
|
715
|
+
if (!task || task.turnId !== expectedTurnId) throw new Error("Chat changed. Refresh and try again.");
|
|
716
|
+
if (scheduled && (!task.scheduled || task.inputSource !== "ordinary")) throw new Error("Only a chat with an active schedule and a human prompt can return to Scheduled.");
|
|
717
|
+
if (!scheduled && done && task.scheduled) throw new Error("Pause all active schedules before marking this chat Done.");
|
|
718
|
+
if (!scheduled && done && task.pullRequests?.some(pr => !mergedPrUrls.includes(pr.url))) throw new Error("Chats with attached PRs require confirmed merge status before marking Done.");
|
|
719
|
+
if (task.observed.archive || task.observed.lastTurnEvent === "inProgress") throw new Error("Archived or in-progress chats cannot be marked from TaskChef.");
|
|
720
|
+
await mkdir(dirname(this.statePath), { recursive: true, mode: 0o700 });
|
|
721
|
+
const release = await acquireWorkspaceLock(dirname(this.statePath));
|
|
722
|
+
try {
|
|
723
|
+
const marks = await readDoneMarks(this.statePath);
|
|
724
|
+
const key = scheduled ? `scheduled:${id}` : id;
|
|
725
|
+
if (done) marks[key] = expectedTurnId;
|
|
726
|
+
else delete marks[key];
|
|
727
|
+
if (!scheduled) {
|
|
728
|
+
if (done && task.pullRequests?.length) marks[`done-pr:${id}`] = mergedPrUrls.join("\n");
|
|
729
|
+
else delete marks[`done-pr:${id}`];
|
|
730
|
+
}
|
|
731
|
+
await writeDurableAtomic(this.statePath, JSON.stringify(marks));
|
|
732
|
+
} finally { await release(); }
|
|
733
|
+
await this.refresh({ force: true });
|
|
734
|
+
return this.task(id);
|
|
735
|
+
});
|
|
736
|
+
this.mutation = action.catch(() => {});
|
|
737
|
+
return action;
|
|
738
|
+
}
|
|
739
|
+
close() {
|
|
740
|
+
this.connections?.history.close();
|
|
741
|
+
this.connections?.state.close();
|
|
742
|
+
this.connections = null;
|
|
743
|
+
this.databaseCache = null;
|
|
744
|
+
this.doneCache = null;
|
|
745
|
+
this.queuedImages = null;
|
|
746
|
+
this.queueImageCache?.clear();
|
|
747
|
+
}
|
|
748
|
+
}
|