@rubytech/create-maxy-code 0.1.158 → 0.1.161
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/package.json +1 -1
- package/payload/platform/lib/admin-conversation-purge/dist/index.d.ts +1 -1
- package/payload/platform/lib/admin-conversation-purge/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/admin-conversation-purge/dist/index.js +4 -4
- package/payload/platform/lib/admin-conversation-purge/dist/index.js.map +1 -1
- package/payload/platform/lib/admin-conversation-purge/src/index.ts +5 -5
- package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +48 -17
- package/payload/platform/lib/graph-style/dist/index.js +2 -2
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +1 -1
- package/payload/platform/lib/graph-style/src/index.ts +2 -2
- package/payload/platform/lib/graph-trash/dist/index.js +1 -1
- package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-trash/src/index.ts +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +3 -3
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js +5 -5
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +1 -1
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +1 -1
- package/payload/platform/lib/graph-write/src/conversation-provenance.ts +8 -8
- package/payload/platform/lib/graph-write/src/index.ts +1 -1
- package/payload/platform/neo4j/schema.cypher +86 -46
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +8 -4
- package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/per-turn-graph-pass-gate.test.sh +361 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-turn-graph-pass.test.sh +13 -13
- package/payload/platform/plugins/admin/hooks/__tests__/turn-completed-graph-write.test.sh +1 -1
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +2 -2
- package/payload/platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh +102 -0
- package/payload/platform/plugins/admin/hooks/per-turn-graph-pass-gate.sh +106 -0
- package/payload/platform/plugins/admin/hooks/pre-turn-graph-pass.sh +15 -8
- package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +4 -4
- package/payload/platform/plugins/admin/mcp/dist/index.js +65 -24
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/session-management/SKILL.md +2 -2
- package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +5 -5
- package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +6 -6
- package/payload/platform/plugins/business-assistant/references/profiling.md +1 -1
- package/payload/platform/plugins/contacts/PLUGIN.md +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +3 -3
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +4 -4
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +10 -10
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -1
- package/payload/platform/plugins/docs/references/admin-session.md +6 -6
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/internals.md +5 -5
- package/payload/platform/plugins/docs/references/plugins-guide.md +6 -5
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +19 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +78 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts +8 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js +177 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.js +2 -2
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +6 -54
- package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js +12 -300
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.d.ts +23 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.js +15 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts +6 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +89 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.js +49 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts +20 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js +91 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +32 -52
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +19 -83
- package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +87 -0
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +10 -37
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +1 -0
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +11 -2
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +139 -439
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +15 -47
- package/payload/platform/plugins/memory/mcp/dist/index.js +197 -58
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js +43 -27
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js +62 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +5 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts +42 -25
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js +23 -17
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +9 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts +3 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js +6 -6
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js +71 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js +31 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +162 -78
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.js +3 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js +141 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.js +8 -8
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js +86 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +33 -34
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +94 -273
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts +43 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js +96 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js +73 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js +4 -4
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +40 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +8 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +25 -9
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +11 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js +46 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +26 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +11 -11
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/scripts/backfill-typed-edges.ts +1 -1
- package/payload/platform/plugins/memory/mcp/scripts/graph/fixture.cypher +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -4
- package/payload/platform/plugins/memory/references/graph-primitives.md +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +8 -10
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +100 -116
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +74 -24
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +1 -1
- package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +1 -1
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +1 -1
- package/payload/platform/plugins/substack-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/substack-import/PLUGIN.md +34 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +182 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/archive-shape.md +68 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/attachments.md +72 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/engagement.md +61 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/posts.md +80 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/subscribers.md +74 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js +6 -6
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js.map +1 -1
- package/payload/platform/plugins/x-import/references/archive-shape.md +2 -2
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +4 -4
- package/payload/platform/scripts/admin-persist-audit.ts +18 -18
- package/payload/platform/scripts/identity-forbidden-token-check.mjs +2 -0
- package/payload/platform/scripts/seed-neo4j.sh +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js +3 -3
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +3 -3
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +4 -4
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +2 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +45 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +5 -17
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-AXOL2PPG.js → chunk-3TRIXQSJ.js} +101 -101
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{admin-3fLFD5h_.js → admin-Dal5FWaD.js} +2 -2
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Breg9-99.js → architectureDiagram-Q4EWVU46-Do2zYONG.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DXf1_673.js → blockDiagram-DXYQGD6D-DpIX_bRu.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CSZ36W4-.js → c4Diagram-AHTNJAMY-Cz335ZSY.js} +1 -1
- package/payload/server/public/assets/channel-PC4_IjI1.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-DsrLk2eK.js → chunk-336JU56O-Dmknn3Wo.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-JpNfNf81.js → chunk-426QAEUC-ClfGjizN.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-DmClEAD0.js → chunk-4TB4RGXK-BPKctJ5c.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BI9blW3N.js → chunk-5FUZZQ4R-BWDCO9zw.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BbgttEFO.js → chunk-5PVQY5BW-BE34_Db0.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-BX_LCFOx.js → chunk-EDXVE4YY-BysA-_6v.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Bb7EoD0w.js → chunk-ENJZ2VHE-BMVyMsqA.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DH8uQk-s.js → chunk-ICPOFSXX-C5KRdLV4.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-FH5kgXsw.js → chunk-OYMX7WX6-DVZD-AVc.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-D_ljlkHr.js → chunk-U2HBQHQK-DYmQdx8x.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-BX3EmZvm.js → chunk-X2U36JSP-DrZenIl3.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-Vu7-E93h.js → chunk-YZCP3GAM-CDuGbvMq.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-CwjhNPX5.js → chunk-ZZ45TVLE-COztbiu5.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B_pMa-SX.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BdBAgF20.js +1 -0
- package/payload/server/public/assets/clone-CkNt9L9r.js +1 -0
- package/payload/server/public/assets/{dagre-Ce6gsj0C.js → dagre-Cv9Ib2OM.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BaMGpsoy.js → dagre-KV5264BT-CoEd61KZ.js} +1 -1
- package/payload/server/public/assets/{data-BHASh0_Y.js → data-DbSORCl4.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-CHEE9rjq.js → diagram-5BDNPKRD-WXNzw3uW.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-C98sZLW1.js → diagram-G4DWMVQ6-C6IuQ2VV.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DNvMGsTZ.js → diagram-MMDJMWI5-CdJdir5A.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Cdte4IBa.js → diagram-TYMM5635-DxWKQH5T.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-C9VZsWXK.js → erDiagram-SMLLAGMA-C9fvs33K.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-tuP5fzzR.js → flowDiagram-DWJPFMVM-BO7Yrbqv.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-SJgRPUwx.js → ganttDiagram-T4ZO3ILL-CQJyWBZq.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DU_cI_50.js → gitGraphDiagram-UUTBAWPF-DZfQHrYw.js} +1 -1
- package/payload/server/public/assets/{graph-labels-DXlmm1og.js → graph-labels-BBB5jsZk.js} +1 -1
- package/payload/server/public/assets/{graph-BhEI09tF.js → graph-ys_joP2o.js} +1 -1
- package/payload/server/public/assets/{graphlib-D_nAsvmx.js → graphlib-C40H-Q5E.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-Cj2Y19m6.js → infoDiagram-42DDH7IO-CakfV5f9.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DMc1xq1y.js → ishikawaDiagram-UXIWVN3A-CPuBPyfa.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-C4CRAQma.js → journeyDiagram-VCZTEJTY-zpZm2Mmo.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CTr4AldU.js → kanban-definition-6JOO6SKY-DfJduQCo.js} +1 -1
- package/payload/server/public/assets/{line-Btlm_IAl.js → line-BpYQqO5R.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-DXFdj_y1.js → mermaid-parser.core-C5Mwi14D.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-DFBhoCr5.js → mermaid.core-D8qBtvvT.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-r9gVQybo.js → mindmap-definition-QFDTVHPH-Bli9JhIN.js} +1 -1
- package/payload/server/public/assets/{page-CDWu1Aft.js → page--bl5Y7ZI.js} +2 -2
- package/payload/server/public/assets/page-CwmZ76U7.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CHKZjSaH.js → pieDiagram-DEJITSTG-MsLXTSLg.js} +1 -1
- package/payload/server/public/assets/{public-DyGWgfaZ.js → public-DltgEfA9.js} +2 -2
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CSGR1HJr.js → quadrantDiagram-34T5L4WZ-De1sh2K0.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DZ6hX9D6.js → requirementDiagram-MS252O5E-D5Sfg1t_.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DbZBKh0m.js → sankeyDiagram-XADWPNL6-DHA3LIbv.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BHHpb8dt.js → sequenceDiagram-FGHM5R23-BE_QlG1B.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DQ1jiLB4.js → stateDiagram-FHFEXIEX-NIwp3F4u.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-pfk2F6st.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BlGiEU7p.js → timeline-definition-GMOUNBTQ-CPE9jyDF.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-DmEH-hsB.js → vennDiagram-DHZGUBPP-CKfoyYmH.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-C7KJJ40d.js → wardleyDiagram-NUSXRM2D-B9yJek-o.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-JDnusCaa.js → xychartDiagram-5P7HB3ND-DuCcSLqX.js} +1 -1
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/public/public.html +1 -1
- package/payload/server/server.js +133 -133
- package/payload/server/public/assets/channel-BmZnt3PP.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-kmMLFUpV.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BV7Ae8ct.js +0 -1
- package/payload/server/public/assets/clone-B5eqoN9I.js +0 -1
- package/payload/server/public/assets/page-cyvshX4Z.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CByT2RzF.js +0 -1
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 440 — per-turn-graph-pass-gate Stop hook test suite.
|
|
3
|
+
#
|
|
4
|
+
# Mirrors session-end-retrospective.test.sh's listener-mock pattern. Local
|
|
5
|
+
# Python HTTP server stands in for /api/admin/log-ingest and records every
|
|
6
|
+
# POST so the assertions read every log line the hook emitted.
|
|
7
|
+
#
|
|
8
|
+
# Contract under test:
|
|
9
|
+
# - Role/specialist gates: exit 0, single trigger-skipped log line, no
|
|
10
|
+
# stderr.
|
|
11
|
+
# role-not-admin | is-specialist | empty-stdin | missing-transcript
|
|
12
|
+
# - No real-user record in transcript (e.g. SessionStart bootstrap, or
|
|
13
|
+
# only tool_result-shaped user records): exit 0, single
|
|
14
|
+
# `trigger-skipped reason=no-real-user` line, no stderr.
|
|
15
|
+
# - Block path — admin's turn since the latest real-user message contains
|
|
16
|
+
# no `Task(database-operator)` dispatch and no `pre-turn-graph-pass-skip`
|
|
17
|
+
# tool_use: exit 2, stderr carries the instruction block,
|
|
18
|
+
# `gate-blocked sessionId=<id> reason=sentinel-absent` emitted.
|
|
19
|
+
# - Release via dispatch — a `Task` tool_use with
|
|
20
|
+
# `input.subagent_type=database-operator` appears AFTER the latest
|
|
21
|
+
# real-user message: exit 0, `gate-released sessionId=<id> via=dispatch`.
|
|
22
|
+
# - Release via skip-sentinel — a `tool_use` named
|
|
23
|
+
# `mcp__admin__pre-turn-graph-pass-skip` appears AFTER the latest
|
|
24
|
+
# real-user message: exit 0,
|
|
25
|
+
# `gate-released sessionId=<id> via=skip-sentinel`.
|
|
26
|
+
# - Per-prompt scoping — a dispatch BEFORE the latest real-user message
|
|
27
|
+
# does NOT release the gate.
|
|
28
|
+
# - Re-entry — a second Stop fired after the release tool_use still
|
|
29
|
+
# releases (the JSONL grep is the re-entry guard, no loop).
|
|
30
|
+
|
|
31
|
+
set -u
|
|
32
|
+
|
|
33
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/per-turn-graph-pass-gate.sh"
|
|
34
|
+
if [[ ! -x "$HOOK" ]]; then
|
|
35
|
+
echo "FAIL: $HOOK not executable" >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
OP_ID='aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb'
|
|
40
|
+
|
|
41
|
+
TMPFILES=()
|
|
42
|
+
LISTENER_PIDS=()
|
|
43
|
+
cleanup_test_state() {
|
|
44
|
+
for pid in "${LISTENER_PIDS[@]:-}"; do
|
|
45
|
+
if [[ -n "$pid" ]]; then
|
|
46
|
+
kill "$pid" 2>/dev/null || true
|
|
47
|
+
wait "$pid" 2>/dev/null || true
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
for f in "${TMPFILES[@]:-}"; do
|
|
51
|
+
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
52
|
+
done
|
|
53
|
+
}
|
|
54
|
+
trap cleanup_test_state EXIT
|
|
55
|
+
|
|
56
|
+
PASS=0
|
|
57
|
+
FAIL=0
|
|
58
|
+
pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
|
59
|
+
fail() { echo "FAIL: $1" >&2; FAIL=$((FAIL + 1)); }
|
|
60
|
+
|
|
61
|
+
start_listener() {
|
|
62
|
+
REQ_LOG=$(mktemp); TMPFILES+=("$REQ_LOG")
|
|
63
|
+
LISTENER_PORT=$((39800 + RANDOM % 100))
|
|
64
|
+
python3 - "$LISTENER_PORT" "$REQ_LOG" <<'PY' &
|
|
65
|
+
import sys, http.server, json
|
|
66
|
+
port = int(sys.argv[1])
|
|
67
|
+
log_path = sys.argv[2]
|
|
68
|
+
class H(http.server.BaseHTTPRequestHandler):
|
|
69
|
+
def log_message(self, *a, **k): pass
|
|
70
|
+
def do_POST(self):
|
|
71
|
+
n = int(self.headers.get('Content-Length','0') or 0)
|
|
72
|
+
body = self.rfile.read(n).decode('utf-8','replace')
|
|
73
|
+
with open(log_path, 'a', encoding='utf-8') as f:
|
|
74
|
+
f.write(self.path + '\t' + body + '\n')
|
|
75
|
+
self.send_response(200)
|
|
76
|
+
self.send_header('Content-Type','application/json')
|
|
77
|
+
self.end_headers()
|
|
78
|
+
self.wfile.write(json.dumps({"ok": True}).encode('utf-8'))
|
|
79
|
+
http.server.HTTPServer(('127.0.0.1', port), H).serve_forever()
|
|
80
|
+
PY
|
|
81
|
+
LISTENER_PIDS+=("$!")
|
|
82
|
+
for _ in $(seq 1 20); do
|
|
83
|
+
if curl -sS --max-time 1 -X POST "http://127.0.0.1:${LISTENER_PORT}/ping" -d '{}' >/dev/null 2>&1; then
|
|
84
|
+
break
|
|
85
|
+
fi
|
|
86
|
+
sleep 0.1
|
|
87
|
+
done
|
|
88
|
+
: > "$REQ_LOG"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
run_hook() {
|
|
92
|
+
local role="$1"; local specialist="$2"; local stdin_json="$3"
|
|
93
|
+
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
94
|
+
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
95
|
+
printf '%s' "$stdin_json" | \
|
|
96
|
+
MAXY_SESSION_ROLE="$role" \
|
|
97
|
+
MAXY_SPECIALIST="$specialist" \
|
|
98
|
+
MAXY_UI_INTERNAL_PORT="$LISTENER_PORT" \
|
|
99
|
+
bash "$HOOK" >"$stdout_file" 2>"$stderr_file"
|
|
100
|
+
HOOK_RC=$?
|
|
101
|
+
HOOK_STDERR=$(cat "$stderr_file")
|
|
102
|
+
HOOK_STDOUT=$(cat "$stdout_file")
|
|
103
|
+
sleep 0.1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ingest_lines() {
|
|
107
|
+
grep -E '^/api/admin/log-ingest ' "$REQ_LOG" 2>/dev/null | python3 -c '
|
|
108
|
+
import sys, json
|
|
109
|
+
for raw in sys.stdin:
|
|
110
|
+
try:
|
|
111
|
+
_, body = raw.rstrip("\n").split("\t", 1)
|
|
112
|
+
d = json.loads(body)
|
|
113
|
+
if isinstance(d, dict) and isinstance(d.get("line"), str):
|
|
114
|
+
print(d["line"])
|
|
115
|
+
except Exception:
|
|
116
|
+
pass
|
|
117
|
+
' || true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# write_transcript: builds JSONL from positional specs.
|
|
121
|
+
# Spec format: <role>|<text>|<extra>
|
|
122
|
+
# extra "" — plain text record
|
|
123
|
+
# extra "tool_result" — user record carrying only a tool_result block
|
|
124
|
+
# extra "tool_use:<NAME>" — assistant record with a tool_use block (no input)
|
|
125
|
+
# extra "task:<subagent_type>" — assistant record with a Task tool_use whose
|
|
126
|
+
# input.subagent_type equals <subagent_type>
|
|
127
|
+
write_transcript() {
|
|
128
|
+
local out="$1"; shift
|
|
129
|
+
python3 - "$out" "$@" <<'PY'
|
|
130
|
+
import sys, json
|
|
131
|
+
out = sys.argv[1]
|
|
132
|
+
spec = sys.argv[2:]
|
|
133
|
+
with open(out, "w", encoding="utf-8") as f:
|
|
134
|
+
for entry in spec:
|
|
135
|
+
role, text, extra = entry.split("|", 2)
|
|
136
|
+
rec = {"type": role, "timestamp": "2026-05-26T10:00:00.000Z"}
|
|
137
|
+
if role == "user":
|
|
138
|
+
if extra == "tool_result":
|
|
139
|
+
rec["message"] = {
|
|
140
|
+
"role": "user",
|
|
141
|
+
"content": [
|
|
142
|
+
{"type": "tool_result", "tool_use_id": "tu_x", "content": text or "ok"},
|
|
143
|
+
],
|
|
144
|
+
}
|
|
145
|
+
else:
|
|
146
|
+
rec["message"] = {"role": "user", "content": text}
|
|
147
|
+
else:
|
|
148
|
+
content_blocks = []
|
|
149
|
+
if text:
|
|
150
|
+
content_blocks.append({"type": "text", "text": text})
|
|
151
|
+
if extra.startswith("tool_use:"):
|
|
152
|
+
tool_name = extra.split(":", 1)[1]
|
|
153
|
+
content_blocks.append({
|
|
154
|
+
"type": "tool_use",
|
|
155
|
+
"id": f"toolu_{tool_name}",
|
|
156
|
+
"name": tool_name,
|
|
157
|
+
"input": {},
|
|
158
|
+
})
|
|
159
|
+
elif extra.startswith("task:"):
|
|
160
|
+
subagent = extra.split(":", 1)[1]
|
|
161
|
+
content_blocks.append({
|
|
162
|
+
"type": "tool_use",
|
|
163
|
+
"id": f"toolu_task_{subagent}",
|
|
164
|
+
"name": "Task",
|
|
165
|
+
"input": {"subagent_type": subagent, "description": "test", "prompt": "test"},
|
|
166
|
+
})
|
|
167
|
+
rec["message"] = {
|
|
168
|
+
"id": f"msg_{role}_{hash(text) & 0xffff:04x}",
|
|
169
|
+
"role": "assistant",
|
|
170
|
+
"content": content_blocks,
|
|
171
|
+
}
|
|
172
|
+
f.write(json.dumps(rec) + "\n")
|
|
173
|
+
PY
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
envelope_for() {
|
|
177
|
+
python3 -c '
|
|
178
|
+
import json, sys
|
|
179
|
+
print(json.dumps({"session_id": sys.argv[1], "transcript_path": sys.argv[2]}))
|
|
180
|
+
' "$1" "$2"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
INSTRUCTION_TOKEN='Before this turn closes, do the per-turn graph pass'
|
|
184
|
+
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
# Case 1: role != admin → trigger-skipped reason=role-not-admin
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
start_listener
|
|
189
|
+
TR_BASIC=$(mktemp); TMPFILES+=("$TR_BASIC")
|
|
190
|
+
write_transcript "$TR_BASIC" "user|hello|" "assistant|hi|"
|
|
191
|
+
ENV_BASIC=$(envelope_for "$OP_ID" "$TR_BASIC")
|
|
192
|
+
: > "$REQ_LOG"
|
|
193
|
+
run_hook "public" "" "$ENV_BASIC"
|
|
194
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-1 rc=$HOOK_RC"
|
|
195
|
+
[[ -z "$HOOK_STDERR" ]] || fail "case-1 stderr must be empty"
|
|
196
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=role-not-admin$"; then
|
|
197
|
+
pass "case-1 role=public → trigger-skipped reason=role-not-admin"
|
|
198
|
+
else
|
|
199
|
+
fail "case-1 expected role-not-admin, got: $(ingest_lines)"
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
# ---------------------------------------------------------------------------
|
|
203
|
+
# Case 2: MAXY_SPECIALIST set → trigger-skipped reason=is-specialist
|
|
204
|
+
# ---------------------------------------------------------------------------
|
|
205
|
+
: > "$REQ_LOG"
|
|
206
|
+
run_hook "admin" "database-operator" "$ENV_BASIC"
|
|
207
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-2 rc=$HOOK_RC"
|
|
208
|
+
[[ -z "$HOOK_STDERR" ]] || fail "case-2 stderr must be empty"
|
|
209
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=is-specialist specialist=database-operator$"; then
|
|
210
|
+
pass "case-2 specialist=database-operator → trigger-skipped is-specialist"
|
|
211
|
+
else
|
|
212
|
+
fail "case-2 expected is-specialist, got: $(ingest_lines)"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
# Case 3: empty stdin → trigger-skipped reason=empty-stdin
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
: > "$REQ_LOG"
|
|
219
|
+
run_hook "admin" "" ""
|
|
220
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-3 rc=$HOOK_RC"
|
|
221
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=.* reason=empty-stdin$"; then
|
|
222
|
+
pass "case-3 empty stdin → trigger-skipped reason=empty-stdin"
|
|
223
|
+
else
|
|
224
|
+
fail "case-3 expected empty-stdin, got: $(ingest_lines)"
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
# Case 4: missing transcript_path → trigger-skipped reason=missing-transcript
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
BAD_ENV=$(python3 -c 'import json,sys; print(json.dumps({"session_id": sys.argv[1]}))' "$OP_ID")
|
|
231
|
+
: > "$REQ_LOG"
|
|
232
|
+
run_hook "admin" "" "$BAD_ENV"
|
|
233
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-4 rc=$HOOK_RC"
|
|
234
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=missing-transcript$"; then
|
|
235
|
+
pass "case-4 missing transcript → trigger-skipped reason=missing-transcript"
|
|
236
|
+
else
|
|
237
|
+
fail "case-4 expected missing-transcript, got: $(ingest_lines)"
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
# Case 5: no real-user record in transcript → trigger-skipped no-real-user
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
243
|
+
TR_NO_USER=$(mktemp); TMPFILES+=("$TR_NO_USER")
|
|
244
|
+
# Only a tool_result-shaped user record + an assistant record. The hook should
|
|
245
|
+
# treat this as "no real user yet" and trigger-skip.
|
|
246
|
+
write_transcript "$TR_NO_USER" "user|ok|tool_result" "assistant|booting|"
|
|
247
|
+
ENV_NO_USER=$(envelope_for "$OP_ID" "$TR_NO_USER")
|
|
248
|
+
: > "$REQ_LOG"
|
|
249
|
+
run_hook "admin" "" "$ENV_NO_USER"
|
|
250
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-5 rc=$HOOK_RC"
|
|
251
|
+
[[ -z "$HOOK_STDERR" ]] || fail "case-5 stderr must be empty"
|
|
252
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=no-real-user$"; then
|
|
253
|
+
pass "case-5 only tool_result-user records → trigger-skipped no-real-user"
|
|
254
|
+
else
|
|
255
|
+
fail "case-5 expected no-real-user, got: $(ingest_lines)"
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
# ---------------------------------------------------------------------------
|
|
259
|
+
# Case 6: gate-blocked — admin replied to operator without dispatching or
|
|
260
|
+
# calling the skip sentinel.
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
TR_BLOCKED=$(mktemp); TMPFILES+=("$TR_BLOCKED")
|
|
263
|
+
write_transcript "$TR_BLOCKED" "user|hello|" "assistant|hi there|"
|
|
264
|
+
ENV_BLOCKED=$(envelope_for "$OP_ID" "$TR_BLOCKED")
|
|
265
|
+
: > "$REQ_LOG"
|
|
266
|
+
run_hook "admin" "" "$ENV_BLOCKED"
|
|
267
|
+
[[ "$HOOK_RC" -eq 2 ]] || fail "case-6 expected rc=2, got $HOOK_RC"
|
|
268
|
+
if ! printf '%s' "$HOOK_STDERR" | grep -qF "$INSTRUCTION_TOKEN"; then
|
|
269
|
+
fail "case-6 stderr must contain instruction token; got: $HOOK_STDERR"
|
|
270
|
+
fi
|
|
271
|
+
if ingest_lines | grep -qE "^gate-blocked sessionId=${OP_ID} reason=sentinel-absent$"; then
|
|
272
|
+
pass "case-6 admin replied without dispatch/skip → gate-blocked + stderr instruction + rc=2"
|
|
273
|
+
else
|
|
274
|
+
fail "case-6 expected gate-blocked, got: $(ingest_lines)"
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
# Case 7: release via dispatch — admin issued Task(database-operator) AFTER
|
|
279
|
+
# the latest real-user message.
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
TR_DISPATCH=$(mktemp); TMPFILES+=("$TR_DISPATCH")
|
|
282
|
+
write_transcript "$TR_DISPATCH" \
|
|
283
|
+
"user|tell me about Alice|" \
|
|
284
|
+
"assistant|dispatching|task:database-operator" \
|
|
285
|
+
"assistant|done|"
|
|
286
|
+
ENV_DISPATCH=$(envelope_for "$OP_ID" "$TR_DISPATCH")
|
|
287
|
+
: > "$REQ_LOG"
|
|
288
|
+
run_hook "admin" "" "$ENV_DISPATCH"
|
|
289
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-7 expected rc=0, got $HOOK_RC"
|
|
290
|
+
[[ -z "$HOOK_STDERR" ]] || fail "case-7 stderr must be empty on release"
|
|
291
|
+
if ingest_lines | grep -qE "^gate-released sessionId=${OP_ID} via=dispatch$"; then
|
|
292
|
+
pass "case-7 Task(database-operator) after latest user → gate-released via=dispatch"
|
|
293
|
+
else
|
|
294
|
+
fail "case-7 expected gate-released via=dispatch, got: $(ingest_lines)"
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# ---------------------------------------------------------------------------
|
|
298
|
+
# Case 8: release via skip-sentinel — admin called the skip MCP tool AFTER
|
|
299
|
+
# the latest real-user message.
|
|
300
|
+
# ---------------------------------------------------------------------------
|
|
301
|
+
TR_SKIP=$(mktemp); TMPFILES+=("$TR_SKIP")
|
|
302
|
+
write_transcript "$TR_SKIP" \
|
|
303
|
+
"user|thanks|" \
|
|
304
|
+
"assistant|noted|tool_use:mcp__admin__pre-turn-graph-pass-skip" \
|
|
305
|
+
"assistant|you're welcome|"
|
|
306
|
+
ENV_SKIP=$(envelope_for "$OP_ID" "$TR_SKIP")
|
|
307
|
+
: > "$REQ_LOG"
|
|
308
|
+
run_hook "admin" "" "$ENV_SKIP"
|
|
309
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-8 expected rc=0, got $HOOK_RC"
|
|
310
|
+
[[ -z "$HOOK_STDERR" ]] || fail "case-8 stderr must be empty on release"
|
|
311
|
+
if ingest_lines | grep -qE "^gate-released sessionId=${OP_ID} via=skip-sentinel$"; then
|
|
312
|
+
pass "case-8 skip-sentinel after latest user → gate-released via=skip-sentinel"
|
|
313
|
+
else
|
|
314
|
+
fail "case-8 expected gate-released via=skip-sentinel, got: $(ingest_lines)"
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
# ---------------------------------------------------------------------------
|
|
318
|
+
# Case 9: per-prompt scoping — a dispatch BEFORE the latest real-user
|
|
319
|
+
# message does NOT release the current turn's gate.
|
|
320
|
+
# ---------------------------------------------------------------------------
|
|
321
|
+
TR_PRIOR=$(mktemp); TMPFILES+=("$TR_PRIOR")
|
|
322
|
+
write_transcript "$TR_PRIOR" \
|
|
323
|
+
"user|first turn|" \
|
|
324
|
+
"assistant|dispatching|task:database-operator" \
|
|
325
|
+
"assistant|done|" \
|
|
326
|
+
"user|second turn|" \
|
|
327
|
+
"assistant|hello again|"
|
|
328
|
+
ENV_PRIOR=$(envelope_for "$OP_ID" "$TR_PRIOR")
|
|
329
|
+
: > "$REQ_LOG"
|
|
330
|
+
run_hook "admin" "" "$ENV_PRIOR"
|
|
331
|
+
[[ "$HOOK_RC" -eq 2 ]] || fail "case-9 expected rc=2, got $HOOK_RC"
|
|
332
|
+
if ingest_lines | grep -qE "^gate-blocked sessionId=${OP_ID} reason=sentinel-absent$"; then
|
|
333
|
+
pass "case-9 dispatch before latest user does NOT release current turn → gate-blocked"
|
|
334
|
+
else
|
|
335
|
+
fail "case-9 expected gate-blocked, got: $(ingest_lines)"
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
# ---------------------------------------------------------------------------
|
|
339
|
+
# Case 10: re-entry — a release tool_use earlier in the same prompt window
|
|
340
|
+
# stays released on a second Stop fire (no loop).
|
|
341
|
+
# ---------------------------------------------------------------------------
|
|
342
|
+
TR_REENTRY=$(mktemp); TMPFILES+=("$TR_REENTRY")
|
|
343
|
+
write_transcript "$TR_REENTRY" \
|
|
344
|
+
"user|hi|" \
|
|
345
|
+
"assistant|dispatching|task:database-operator" \
|
|
346
|
+
"assistant|done|" \
|
|
347
|
+
"assistant|wrap-up reply|"
|
|
348
|
+
ENV_REENTRY=$(envelope_for "$OP_ID" "$TR_REENTRY")
|
|
349
|
+
: > "$REQ_LOG"
|
|
350
|
+
run_hook "admin" "" "$ENV_REENTRY"
|
|
351
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-10 expected rc=0, got $HOOK_RC"
|
|
352
|
+
if ingest_lines | grep -qE "^gate-released sessionId=${OP_ID} via=dispatch$"; then
|
|
353
|
+
pass "case-10 second Stop after dispatch still releases (no loop)"
|
|
354
|
+
else
|
|
355
|
+
fail "case-10 expected gate-released via=dispatch, got: $(ingest_lines)"
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
# ---------------------------------------------------------------------------
|
|
359
|
+
echo
|
|
360
|
+
echo "per-turn-graph-pass-gate tests: $PASS passed, $FAIL failed"
|
|
361
|
+
[[ "$FAIL" -eq 0 ]]
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
# "additionalContext":"..."}}` whose additionalContext begins with
|
|
27
27
|
# `[system: pre-turn-graph-pass-hook]`, names `database-operator` as
|
|
28
28
|
# the dispatch target, carries a `<prior-writes>` block with the
|
|
29
|
-
# fetched node, interpolates
|
|
29
|
+
# fetched node, interpolates sessionId from SESSION_NODE_ID,
|
|
30
30
|
# and carries NO `<conversation>` block (admin has the turn already).
|
|
31
31
|
# Dispatch log line carries `directive-shape=admin-walks`.
|
|
32
32
|
# - Prior-writes fetch fails (context endpoint 500): exit 0, dispatch
|
|
33
33
|
# still fires, `<prior-writes>` block resolves to `(none)`.
|
|
34
|
-
# -
|
|
35
|
-
# `
|
|
34
|
+
# - SESSION_NODE_ID unset: exit 0, additionalContext names
|
|
35
|
+
# `sessionId: <unset>`, and no GET to /api/admin/post-turn-context
|
|
36
36
|
# is attempted.
|
|
37
37
|
# - UserPromptSubmit stdin-shape regression: a synthetic UserPromptSubmit
|
|
38
38
|
# payload produces the documented stdout envelope; a stale Stop-shaped
|
|
@@ -125,7 +125,7 @@ PY
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
run_hook() {
|
|
128
|
-
# Args: role, specialist, ui_port,
|
|
128
|
+
# Args: role, specialist, ui_port, session_node_id, stdin_json
|
|
129
129
|
local role="$1"; local specialist="$2"; local ui_port="$3"
|
|
130
130
|
local conv="$4"; local stdin_json="$5"
|
|
131
131
|
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
@@ -137,7 +137,7 @@ run_hook() {
|
|
|
137
137
|
env_cmd+=("MAXY_UI_INTERNAL_PORT=${ui_port}")
|
|
138
138
|
fi
|
|
139
139
|
if [[ -n "$conv" ]]; then
|
|
140
|
-
env_cmd+=("
|
|
140
|
+
env_cmd+=("SESSION_NODE_ID=${conv}")
|
|
141
141
|
fi
|
|
142
142
|
env_cmd+=("ACCOUNT_ID=acct-test-0001")
|
|
143
143
|
printf '%s' "$stdin_json" | "${env_cmd[@]}" bash "$HOOK" >"$stdout_file" 2>"$stderr_file"
|
|
@@ -328,14 +328,14 @@ fi
|
|
|
328
328
|
if ! printf '%s' "$CTX_BODY" | grep -q 'database-operator'; then
|
|
329
329
|
fail "case-5 additionalContext must name the database-operator dispatch target"
|
|
330
330
|
fi
|
|
331
|
-
if ! printf '%s' "$CTX_BODY" | grep -q "
|
|
332
|
-
fail "case-5 additionalContext must interpolate
|
|
331
|
+
if ! printf '%s' "$CTX_BODY" | grep -q "sessionId: ${CONV_ID}"; then
|
|
332
|
+
fail "case-5 additionalContext must interpolate sessionId=${CONV_ID}"
|
|
333
333
|
fi
|
|
334
334
|
if ! printf '%s' "$CTX_BODY" | grep -q 'KnowledgeDocument'; then
|
|
335
335
|
fail "case-5 additionalContext <prior-writes> must include fetched labels"
|
|
336
336
|
fi
|
|
337
|
-
if ingest_lines | grep -qE "^dispatch
|
|
338
|
-
pass "case-5 dispatch path → exit 0 + JSON envelope with prior-writes,
|
|
337
|
+
if ingest_lines | grep -qE "^dispatch claudeSessionId=${OP_ID} sessionId=${CONV_ID} directive-shape=admin-walks "; then
|
|
338
|
+
pass "case-5 dispatch path → exit 0 + JSON envelope with prior-writes, sessionId, admin-walks shape"
|
|
339
339
|
else
|
|
340
340
|
fail "case-5 expected dispatch log line with directive-shape=admin-walks, got: $(ingest_lines)"
|
|
341
341
|
fi
|
|
@@ -356,7 +356,7 @@ else
|
|
|
356
356
|
fi
|
|
357
357
|
|
|
358
358
|
# ---------------------------------------------------------------------------
|
|
359
|
-
# Case 7:
|
|
359
|
+
# Case 7: SESSION_NODE_ID unset → sessionId:<unset>, no context GET.
|
|
360
360
|
# ---------------------------------------------------------------------------
|
|
361
361
|
cat > "$CTX_FILE" <<'JSON'
|
|
362
362
|
{"writes":[]}
|
|
@@ -365,12 +365,12 @@ JSON
|
|
|
365
365
|
run_hook "admin" "" "$LISTENER_PORT" "" "$ENV_BASIC"
|
|
366
366
|
[[ "$HOOK_RC" -eq 0 ]] || fail "case-7 expected rc=0, got $HOOK_RC"
|
|
367
367
|
CTX_BODY=$(additional_context)
|
|
368
|
-
if ! printf '%s' "$CTX_BODY" | grep -q '
|
|
369
|
-
fail "case-7 additionalContext must contain '
|
|
368
|
+
if ! printf '%s' "$CTX_BODY" | grep -q 'sessionId: <unset>'; then
|
|
369
|
+
fail "case-7 additionalContext must contain 'sessionId: <unset>'"
|
|
370
370
|
fi
|
|
371
371
|
GET_COUNT=$(context_get_count)
|
|
372
372
|
if [[ "$GET_COUNT" == "0" ]]; then
|
|
373
|
-
pass "case-7
|
|
373
|
+
pass "case-7 SESSION_NODE_ID unset → no GET /api/admin/post-turn-context attempted"
|
|
374
374
|
else
|
|
375
375
|
fail "case-7 expected zero context GETs, got: $GET_COUNT"
|
|
376
376
|
fi
|
|
@@ -194,7 +194,7 @@ fi
|
|
|
194
194
|
# --- Case 5 (Task 177): ordered-turns happy path -----------------------
|
|
195
195
|
# Transcript with four turns: user-A, asst-A-reply, user-B, asst-B-reply.
|
|
196
196
|
# Envelope `turns` array must carry exactly that order, oldest first.
|
|
197
|
-
# Envelope shape: { turns,
|
|
197
|
+
# Envelope shape: { turns, sessionId, accountId, occurredAt } — no
|
|
198
198
|
# top-level operatorMessage / assistantReply.
|
|
199
199
|
ORDERED_TRANSCRIPT=$(mktemp); TMPFILES+=("$ORDERED_TRANSCRIPT")
|
|
200
200
|
{
|
|
@@ -154,7 +154,7 @@ fi
|
|
|
154
154
|
case "$TOOL_NAME" in
|
|
155
155
|
mcp__memory__whatsapp-export-parse|mcp__memory__whatsapp-export-insight-write|mcp__memory__whatsapp-export-insight-pass|mcp__memory__whatsapp-export-preview)
|
|
156
156
|
emit_decision "block" "denied-mcp-legacy" \
|
|
157
|
-
"Blocked: ${TOOL_NAME} is a retired path. generalised conversation-archive
|
|
157
|
+
"Blocked: ${TOOL_NAME} is a retired path. Invoke the generalised conversation-archive skill — 'bash platform/plugins/memory/bin/conversation-archive-ingest.sh <archive> --source whatsapp --participant-person-ids <csv> --scope <admin|public>' (owner derived from ACCOUNT_ID + USER_ID env). The script normalises, sessionizes, and emits prepared sessions; the dispatched specialist classifies each session in-turn and calls memory-ingest with conversationIdentity set (writes :ConversationArchive)."
|
|
158
158
|
;;
|
|
159
159
|
esac
|
|
160
160
|
|
|
@@ -186,7 +186,7 @@ if [ "$TOOL_NAME" = "mcp__memory__memory-archive-write" ]; then
|
|
|
186
186
|
ARCHIVE_TYPE=$(extract_tool_input_field archiveType)
|
|
187
187
|
if [ "$ARCHIVE_TYPE" = "whatsapp-export" ]; then
|
|
188
188
|
emit_decision "block" "denied-mcp-legacy archiveType=whatsapp-export" \
|
|
189
|
-
"Blocked: memory-archive-write with archiveType='whatsapp-export' is a retired path.
|
|
189
|
+
"Blocked: memory-archive-write with archiveType='whatsapp-export' is a retired path. Routes conversation transcripts through 'bash platform/plugins/memory/bin/conversation-archive-ingest.sh <archive> --source whatsapp --participant-person-ids <csv> --scope <admin|public>' (owner derived from ACCOUNT_ID + USER_ID env). Flat-dataset archiveTypes (linkedin-connections, …) flow through memory-archive-write unchanged."
|
|
190
190
|
fi
|
|
191
191
|
fi
|
|
192
192
|
|
|
@@ -138,3 +138,105 @@ agp_guard_transcript() {
|
|
|
138
138
|
exit 0
|
|
139
139
|
fi
|
|
140
140
|
}
|
|
141
|
+
|
|
142
|
+
# Walks $TRANSCRIPT_PATH and reports, in one line on stdout:
|
|
143
|
+
# <real-user-present:yes|no>\t<matched-tool-name-or-empty>
|
|
144
|
+
# A "real user" record matches `session-end-retrospective.sh`'s `is_real_user`
|
|
145
|
+
# — `type=user, message.role=user`, content is a string OR a list with at
|
|
146
|
+
# least one non-`tool_result` block. `tool_result`-only user records are tool
|
|
147
|
+
# replies, never turn starts.
|
|
148
|
+
#
|
|
149
|
+
# Every assistant `tool_use` block that appears AFTER the latest real-user
|
|
150
|
+
# record is matched against the alternation passed as `$1` (an extended-regex
|
|
151
|
+
# alternation of literal `tool_use.name` values). The synthetic name
|
|
152
|
+
# `Task:<subagent_type>` is also matched, so callers can release on a
|
|
153
|
+
# `Task` tool_use with `input.subagent_type=<name>` by including
|
|
154
|
+
# `Task:<name>` in the alternation. Returns the first match encountered after
|
|
155
|
+
# the latest real-user record (oldest-first) so the release log carries the
|
|
156
|
+
# matched tool's actual name. Empty match means none of the release patterns
|
|
157
|
+
# appeared since the operator's last turn.
|
|
158
|
+
agp_scan_post_user_toolnames() {
|
|
159
|
+
local pattern="$1"
|
|
160
|
+
TRANSCRIPT_PATH="$TRANSCRIPT_PATH" AGP_PATTERN="$pattern" python3 - <<'PY'
|
|
161
|
+
import os, json, re
|
|
162
|
+
|
|
163
|
+
pattern = re.compile(os.environ["AGP_PATTERN"])
|
|
164
|
+
path = os.environ["TRANSCRIPT_PATH"]
|
|
165
|
+
|
|
166
|
+
def is_real_user(rec):
|
|
167
|
+
if not isinstance(rec, dict) or rec.get("type") != "user":
|
|
168
|
+
return False
|
|
169
|
+
msg = rec.get("message")
|
|
170
|
+
if not isinstance(msg, dict) or msg.get("role") != "user":
|
|
171
|
+
return False
|
|
172
|
+
content = msg.get("content")
|
|
173
|
+
if isinstance(content, str):
|
|
174
|
+
return True
|
|
175
|
+
if isinstance(content, list):
|
|
176
|
+
for b in content:
|
|
177
|
+
if isinstance(b, dict) and b.get("type") != "tool_result":
|
|
178
|
+
return True
|
|
179
|
+
return False
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
def assistant_tool_use_names(rec):
|
|
183
|
+
if not isinstance(rec, dict) or rec.get("type") != "assistant":
|
|
184
|
+
return []
|
|
185
|
+
msg = rec.get("message", {}) or {}
|
|
186
|
+
if msg.get("role") != "assistant":
|
|
187
|
+
return []
|
|
188
|
+
content = msg.get("content")
|
|
189
|
+
if not isinstance(content, list):
|
|
190
|
+
return []
|
|
191
|
+
names = []
|
|
192
|
+
for b in content:
|
|
193
|
+
if isinstance(b, dict) and b.get("type") == "tool_use":
|
|
194
|
+
n = b.get("name")
|
|
195
|
+
if not isinstance(n, str):
|
|
196
|
+
continue
|
|
197
|
+
names.append(n)
|
|
198
|
+
if n == "Task":
|
|
199
|
+
inp = b.get("input")
|
|
200
|
+
if isinstance(inp, dict):
|
|
201
|
+
st = inp.get("subagent_type")
|
|
202
|
+
if isinstance(st, str) and st:
|
|
203
|
+
names.append(f"Task:{st}")
|
|
204
|
+
return names
|
|
205
|
+
|
|
206
|
+
records = []
|
|
207
|
+
try:
|
|
208
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
209
|
+
for raw in f:
|
|
210
|
+
raw = raw.strip()
|
|
211
|
+
if not raw:
|
|
212
|
+
continue
|
|
213
|
+
try:
|
|
214
|
+
records.append(json.loads(raw))
|
|
215
|
+
except Exception:
|
|
216
|
+
continue
|
|
217
|
+
except Exception:
|
|
218
|
+
print("no\t")
|
|
219
|
+
raise SystemExit(0)
|
|
220
|
+
|
|
221
|
+
latest_user_idx = -1
|
|
222
|
+
for i in range(len(records) - 1, -1, -1):
|
|
223
|
+
if is_real_user(records[i]):
|
|
224
|
+
latest_user_idx = i
|
|
225
|
+
break
|
|
226
|
+
|
|
227
|
+
if latest_user_idx == -1:
|
|
228
|
+
print("no\t")
|
|
229
|
+
raise SystemExit(0)
|
|
230
|
+
|
|
231
|
+
matched = ""
|
|
232
|
+
for r in records[latest_user_idx + 1:]:
|
|
233
|
+
for n in assistant_tool_use_names(r):
|
|
234
|
+
if pattern.fullmatch(n):
|
|
235
|
+
matched = n
|
|
236
|
+
break
|
|
237
|
+
if matched:
|
|
238
|
+
break
|
|
239
|
+
|
|
240
|
+
print(f"yes\t{matched}")
|
|
241
|
+
PY
|
|
242
|
+
}
|