opensquid 0.5.441 → 0.5.449
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/functions/arm_scope.d.ts +27 -0
- package/dist/functions/arm_scope.d.ts.map +1 -0
- package/dist/functions/arm_scope.js +52 -0
- package/dist/functions/arm_scope.js.map +1 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -0
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/recall_pre_inject.d.ts.map +1 -1
- package/dist/functions/recall_pre_inject.js +12 -0
- package/dist/functions/recall_pre_inject.js.map +1 -1
- package/dist/rag/store_git.d.ts +23 -0
- package/dist/rag/store_git.d.ts.map +1 -0
- package/dist/rag/store_git.js +57 -0
- package/dist/rag/store_git.js.map +1 -0
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +2 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/handoff/render.d.ts +5 -4
- package/dist/runtime/handoff/render.d.ts.map +1 -1
- package/dist/runtime/handoff/render.js +7 -7
- package/dist/runtime/handoff/render.js.map +1 -1
- package/dist/runtime/hooks/active_task_mirror.js +0 -0
- package/dist/runtime/hooks/apply_patch.js +0 -0
- package/dist/runtime/hooks/dispatch.js +0 -0
- package/dist/runtime/hooks/hook_output.js +0 -0
- package/dist/runtime/hooks/memory_reconcile.js +0 -0
- package/dist/runtime/hooks/new_project_detect.js +0 -0
- package/dist/runtime/hooks/profession_resolver.js +0 -0
- package/dist/runtime/hooks/scope_intent.js +0 -0
- package/dist/runtime/hooks/session-end.js +11 -0
- package/dist/runtime/hooks/session-end.js.map +1 -1
- package/dist/runtime/hooks/session_id.js +0 -0
- package/dist/runtime/hooks/session_liveness.js +0 -0
- package/dist/runtime/hooks/stop_drive.js +0 -0
- package/dist/runtime/hooks/stop_stream.js +0 -0
- package/dist/runtime/hooks/subagent_guard.js +0 -0
- package/dist/runtime/hooks/transcript.js +0 -0
- package/dist/runtime/hooks/transcript_tasks.js +0 -0
- package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
- package/dist/runtime/ralph/orchestrator.js +2 -1
- package/dist/runtime/ralph/orchestrator.js.map +1 -1
- package/dist/setup/cli/limits_state.d.ts.map +1 -1
- package/dist/setup/cli/limits_state.js +6 -40
- package/dist/setup/cli/limits_state.js.map +1 -1
- package/dist/setup/cli/pack_walk.d.ts +32 -0
- package/dist/setup/cli/pack_walk.d.ts.map +1 -0
- package/dist/setup/cli/pack_walk.js +76 -0
- package/dist/setup/cli/pack_walk.js.map +1 -0
- package/dist/setup/cli/permissions_state.d.ts.map +1 -1
- package/dist/setup/cli/permissions_state.js +6 -37
- package/dist/setup/cli/permissions_state.js.map +1 -1
- package/dist/setup/cli/triggers_state.d.ts.map +1 -1
- package/dist/setup/cli/triggers_state.js +3 -29
- package/dist/setup/cli/triggers_state.js.map +1 -1
- package/dist/workgraph/events.d.ts.map +1 -1
- package/dist/workgraph/events.js +10 -0
- package/dist/workgraph/events.js.map +1 -1
- package/dist/workgraph/store.d.ts.map +1 -1
- package/dist/workgraph/store.js +5 -0
- package/dist/workgraph/store.js.map +1 -1
- package/dist/workgraph/types.d.ts +2 -1
- package/dist/workgraph/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +268 -0
- package/docs/pack-runtime.md +15 -12
- package/docs/skill-grammar-guide.md +4 -4
- package/package.json +5 -3
- package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
- package/dist/anti-drift/evaluator.d.ts +0 -88
- package/dist/anti-drift/evaluator.d.ts.map +0 -1
- package/dist/anti-drift/evaluator.js +0 -417
- package/dist/anti-drift/evaluator.js.map +0 -1
- package/dist/anti-drift/evaluator.test.js +0 -78
- package/dist/anti-drift/rules.d.ts +0 -80
- package/dist/anti-drift/rules.d.ts.map +0 -1
- package/dist/anti-drift/rules.js +0 -368
- package/dist/anti-drift/rules.js.map +0 -1
- package/dist/anti-drift/rules.test.js +0 -213
- package/dist/anti-drift/state.d.ts +0 -107
- package/dist/anti-drift/state.d.ts.map +0 -1
- package/dist/anti-drift/state.js +0 -177
- package/dist/anti-drift/state.js.map +0 -1
- package/dist/anti-drift/state.test.js +0 -120
- package/dist/chat/adapters/discord.d.ts +0 -41
- package/dist/chat/adapters/discord.d.ts.map +0 -1
- package/dist/chat/adapters/discord.js +0 -176
- package/dist/chat/adapters/discord.js.map +0 -1
- package/dist/chat/adapters/discord.test.js +0 -25
- package/dist/chat/adapters/slack.d.ts +0 -43
- package/dist/chat/adapters/slack.d.ts.map +0 -1
- package/dist/chat/adapters/slack.js +0 -172
- package/dist/chat/adapters/slack.js.map +0 -1
- package/dist/chat/adapters/slack.test.js +0 -30
- package/dist/chat/adapters/telegram.d.ts +0 -148
- package/dist/chat/adapters/telegram.d.ts.map +0 -1
- package/dist/chat/adapters/telegram.js +0 -498
- package/dist/chat/adapters/telegram.js.map +0 -1
- package/dist/chat/adapters/telegram.test.js +0 -94
- package/dist/chat/config.d.ts +0 -98
- package/dist/chat/config.d.ts.map +0 -1
- package/dist/chat/config.js +0 -185
- package/dist/chat/config.js.map +0 -1
- package/dist/chat/daemon/active-project.d.ts +0 -17
- package/dist/chat/daemon/active-project.d.ts.map +0 -1
- package/dist/chat/daemon/active-project.js +0 -23
- package/dist/chat/daemon/active-project.js.map +0 -1
- package/dist/chat/daemon/autospawn.d.ts +0 -40
- package/dist/chat/daemon/autospawn.d.ts.map +0 -1
- package/dist/chat/daemon/autospawn.js +0 -129
- package/dist/chat/daemon/autospawn.js.map +0 -1
- package/dist/chat/daemon/autospawn.test.js +0 -112
- package/dist/chat/daemon/cli.d.ts +0 -18
- package/dist/chat/daemon/cli.d.ts.map +0 -1
- package/dist/chat/daemon/cli.js +0 -71
- package/dist/chat/daemon/cli.js.map +0 -1
- package/dist/chat/daemon/collisions.js +0 -384
- package/dist/chat/daemon/health-check.d.ts +0 -69
- package/dist/chat/daemon/health-check.d.ts.map +0 -1
- package/dist/chat/daemon/health-check.js +0 -112
- package/dist/chat/daemon/health-check.js.map +0 -1
- package/dist/chat/daemon/inbox-read.d.ts +0 -35
- package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
- package/dist/chat/daemon/inbox-read.js +0 -75
- package/dist/chat/daemon/inbox-read.js.map +0 -1
- package/dist/chat/daemon/inbox-read.test.js +0 -97
- package/dist/chat/daemon/inbox.d.ts +0 -63
- package/dist/chat/daemon/inbox.d.ts.map +0 -1
- package/dist/chat/daemon/inbox.js +0 -56
- package/dist/chat/daemon/inbox.js.map +0 -1
- package/dist/chat/daemon/inbox.test.js +0 -110
- package/dist/chat/daemon/lifecycle.d.ts +0 -71
- package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
- package/dist/chat/daemon/lifecycle.js +0 -221
- package/dist/chat/daemon/lifecycle.js.map +0 -1
- package/dist/chat/daemon/lifecycle.test.js +0 -163
- package/dist/chat/daemon/protocol.d.ts +0 -107
- package/dist/chat/daemon/protocol.d.ts.map +0 -1
- package/dist/chat/daemon/protocol.js +0 -54
- package/dist/chat/daemon/protocol.js.map +0 -1
- package/dist/chat/daemon/routing.d.ts +0 -140
- package/dist/chat/daemon/routing.d.ts.map +0 -1
- package/dist/chat/daemon/routing.js +0 -198
- package/dist/chat/daemon/routing.js.map +0 -1
- package/dist/chat/daemon/routing.test.js +0 -259
- package/dist/chat/daemon/rpc-client.d.ts +0 -45
- package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-client.js +0 -133
- package/dist/chat/daemon/rpc-client.js.map +0 -1
- package/dist/chat/daemon/rpc-server.d.ts +0 -39
- package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-server.js +0 -385
- package/dist/chat/daemon/rpc-server.js.map +0 -1
- package/dist/chat/daemon/rpc.test.js +0 -177
- package/dist/chat/daemon/subscribers.js +0 -257
- package/dist/chat/daemon/worker.d.ts +0 -27
- package/dist/chat/daemon/worker.d.ts.map +0 -1
- package/dist/chat/daemon/worker.js +0 -313
- package/dist/chat/daemon/worker.js.map +0 -1
- package/dist/chat/daemon/workspace-topic.js +0 -324
- package/dist/chat/env-token.d.ts +0 -60
- package/dist/chat/env-token.d.ts.map +0 -1
- package/dist/chat/env-token.js +0 -137
- package/dist/chat/env-token.js.map +0 -1
- package/dist/chat/env-token.test.js +0 -160
- package/dist/chat/factory.d.ts +0 -30
- package/dist/chat/factory.d.ts.map +0 -1
- package/dist/chat/factory.js +0 -50
- package/dist/chat/factory.js.map +0 -1
- package/dist/chat/factory.test.js +0 -55
- package/dist/chat/gateway.d.ts +0 -176
- package/dist/chat/gateway.d.ts.map +0 -1
- package/dist/chat/gateway.js +0 -146
- package/dist/chat/gateway.js.map +0 -1
- package/dist/chat/gateway.test.js +0 -192
- package/dist/claude-md.d.ts +0 -39
- package/dist/claude-md.d.ts.map +0 -1
- package/dist/claude-md.js +0 -113
- package/dist/claude-md.js.map +0 -1
- package/dist/claude-md.test.js +0 -91
- package/dist/codex/activate.d.ts +0 -66
- package/dist/codex/activate.d.ts.map +0 -1
- package/dist/codex/activate.js +0 -329
- package/dist/codex/activate.js.map +0 -1
- package/dist/codex/activate.test.js +0 -229
- package/dist/codex/bundled-default/bundled-default.test.js +0 -161
- package/dist/codex/cli-publish.test.js +0 -133
- package/dist/codex/cli.d.ts +0 -35
- package/dist/codex/cli.d.ts.map +0 -1
- package/dist/codex/cli.js +0 -554
- package/dist/codex/cli.js.map +0 -1
- package/dist/codex/cli.test.js +0 -277
- package/dist/codex/import-skill-md.d.ts +0 -53
- package/dist/codex/import-skill-md.d.ts.map +0 -1
- package/dist/codex/import-skill-md.js +0 -236
- package/dist/codex/import-skill-md.js.map +0 -1
- package/dist/codex/import-skill-md.test.js +0 -225
- package/dist/codex/loader.d.ts +0 -27
- package/dist/codex/loader.d.ts.map +0 -1
- package/dist/codex/loader.js +0 -86
- package/dist/codex/loader.js.map +0 -1
- package/dist/codex/loader.test.js +0 -75
- package/dist/codex/parse.d.ts +0 -28
- package/dist/codex/parse.d.ts.map +0 -1
- package/dist/codex/parse.js +0 -309
- package/dist/codex/parse.js.map +0 -1
- package/dist/codex/parse.test.js +0 -241
- package/dist/codex/store.d.ts +0 -87
- package/dist/codex/store.d.ts.map +0 -1
- package/dist/codex/store.js +0 -205
- package/dist/codex/store.js.map +0 -1
- package/dist/codex/store.test.js +0 -242
- package/dist/codex/types.d.ts +0 -398
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -21
- package/dist/codex/types.js.map +0 -1
- package/dist/config.d.ts +0 -53
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -202
- package/dist/config.js.map +0 -1
- package/dist/config.test.js +0 -117
- package/dist/engine/cli.d.ts +0 -14
- package/dist/engine/cli.d.ts.map +0 -1
- package/dist/engine/cli.js +0 -171
- package/dist/engine/cli.js.map +0 -1
- package/dist/engine/client.d.ts +0 -219
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js +0 -312
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/config.d.ts +0 -62
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js +0 -223
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/index.d.ts +0 -17
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -16
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/resolver.d.ts +0 -62
- package/dist/engine/resolver.d.ts.map +0 -1
- package/dist/engine/resolver.js +0 -103
- package/dist/engine/resolver.js.map +0 -1
- package/dist/engine/singleton.d.ts +0 -95
- package/dist/engine/singleton.d.ts.map +0 -1
- package/dist/engine/singleton.js +0 -325
- package/dist/engine/singleton.js.map +0 -1
- package/dist/engine/types.d.ts +0 -402
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -22
- package/dist/engine/types.js.map +0 -1
- package/dist/engine-binary-resolver.js +0 -110
- package/dist/engine-binary-resolver.test.js +0 -61
- package/dist/engine-cli.js +0 -60
- package/dist/engine-client.js +0 -301
- package/dist/engine-client.test.js +0 -118
- package/dist/functions/chain_state.d.ts +0 -51
- package/dist/functions/chain_state.d.ts.map +0 -1
- package/dist/functions/chain_state.js +0 -59
- package/dist/functions/chain_state.js.map +0 -1
- package/dist/hooks/drift-catalog.d.ts +0 -68
- package/dist/hooks/drift-catalog.d.ts.map +0 -1
- package/dist/hooks/drift-catalog.js +0 -184
- package/dist/hooks/drift-catalog.js.map +0 -1
- package/dist/hooks/drift-catalog.test.js +0 -154
- package/dist/hooks/drift-patterns.d.ts +0 -110
- package/dist/hooks/drift-patterns.d.ts.map +0 -1
- package/dist/hooks/drift-patterns.js +0 -289
- package/dist/hooks/drift-patterns.js.map +0 -1
- package/dist/hooks/drift-patterns.test.js +0 -325
- package/dist/hooks/engine-vocab-gate.d.ts +0 -108
- package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
- package/dist/hooks/engine-vocab-gate.js +0 -225
- package/dist/hooks/engine-vocab-gate.js.map +0 -1
- package/dist/hooks/engine-vocab-gate.test.js +0 -170
- package/dist/hooks/heartbeat.d.ts +0 -107
- package/dist/hooks/heartbeat.d.ts.map +0 -1
- package/dist/hooks/heartbeat.js +0 -316
- package/dist/hooks/heartbeat.js.map +0 -1
- package/dist/hooks/heartbeat.test.js +0 -393
- package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
- package/dist/hooks/honesty-ledger.d.ts +0 -123
- package/dist/hooks/honesty-ledger.d.ts.map +0 -1
- package/dist/hooks/honesty-ledger.js +0 -226
- package/dist/hooks/honesty-ledger.js.map +0 -1
- package/dist/hooks/honesty-ledger.test.js +0 -466
- package/dist/hooks/inline-report-check.d.ts +0 -63
- package/dist/hooks/inline-report-check.d.ts.map +0 -1
- package/dist/hooks/inline-report-check.js +0 -88
- package/dist/hooks/inline-report-check.js.map +0 -1
- package/dist/hooks/inline-report-check.test.js +0 -96
- package/dist/hooks/pre-tool-use.d.ts +0 -62
- package/dist/hooks/pre-tool-use.d.ts.map +0 -1
- package/dist/hooks/pre-tool-use.js +0 -342
- package/dist/hooks/pre-tool-use.js.map +0 -1
- package/dist/hooks/pre-tool-use.test.js +0 -134
- package/dist/hooks/session-end.d.ts +0 -15
- package/dist/hooks/session-end.d.ts.map +0 -1
- package/dist/hooks/session-end.js +0 -60
- package/dist/hooks/session-end.js.map +0 -1
- package/dist/hooks/session-end.test.js +0 -52
- package/dist/hooks/stop.d.ts +0 -35
- package/dist/hooks/stop.d.ts.map +0 -1
- package/dist/hooks/stop.js +0 -136
- package/dist/hooks/stop.js.map +0 -1
- package/dist/hooks/transcript-active-task.test.js +0 -342
- package/dist/hooks/transcript.d.ts +0 -26
- package/dist/hooks/transcript.d.ts.map +0 -1
- package/dist/hooks/transcript.js +0 -266
- package/dist/hooks/transcript.js.map +0 -1
- package/dist/hooks/transcript.test.js +0 -103
- package/dist/hooks/user-prompt-submit.d.ts +0 -74
- package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
- package/dist/hooks/user-prompt-submit.js +0 -256
- package/dist/hooks/user-prompt-submit.js.map +0 -1
- package/dist/hooks/user-prompt-submit.test.js +0 -118
- package/dist/hooks/versioning-gate.d.ts +0 -101
- package/dist/hooks/versioning-gate.d.ts.map +0 -1
- package/dist/hooks/versioning-gate.js +0 -245
- package/dist/hooks/versioning-gate.js.map +0 -1
- package/dist/hooks/versioning-gate.test.js +0 -368
- package/dist/hooks/workflow-gate.d.ts +0 -64
- package/dist/hooks/workflow-gate.d.ts.map +0 -1
- package/dist/hooks/workflow-gate.js +0 -152
- package/dist/hooks/workflow-gate.js.map +0 -1
- package/dist/hooks/workflow-gate.test.js +0 -197
- package/dist/hooks-cli.d.ts +0 -25
- package/dist/hooks-cli.d.ts.map +0 -1
- package/dist/hooks-cli.js +0 -286
- package/dist/hooks-cli.js.map +0 -1
- package/dist/hooks-cli.test.js +0 -148
- package/dist/origin.d.ts +0 -16
- package/dist/origin.d.ts.map +0 -1
- package/dist/origin.js +0 -92
- package/dist/origin.js.map +0 -1
- package/dist/packs/seed_lessons_ingest.d.ts +0 -30
- package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
- package/dist/packs/seed_lessons_ingest.js +0 -107
- package/dist/packs/seed_lessons_ingest.js.map +0 -1
- package/dist/project-cli.d.ts +0 -7
- package/dist/project-cli.d.ts.map +0 -1
- package/dist/project-cli.js +0 -145
- package/dist/project-cli.js.map +0 -1
- package/dist/project.d.ts +0 -127
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -281
- package/dist/project.js.map +0 -1
- package/dist/project.test.js +0 -287
- package/dist/rag/backends/loop_engine.d.ts +0 -61
- package/dist/rag/backends/loop_engine.d.ts.map +0 -1
- package/dist/rag/backends/loop_engine.js +0 -160
- package/dist/rag/backends/loop_engine.js.map +0 -1
- package/dist/recall.d.ts +0 -82
- package/dist/recall.d.ts.map +0 -1
- package/dist/recall.js +0 -81
- package/dist/recall.js.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
- package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.js +0 -251
- package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
- package/dist/runtime/chain_state.d.ts +0 -124
- package/dist/runtime/chain_state.d.ts.map +0 -1
- package/dist/runtime/chain_state.js +0 -189
- package/dist/runtime/chain_state.js.map +0 -1
- package/dist/runtime/hooks/permission_decision.d.ts +0 -34
- package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
- package/dist/runtime/hooks/permission_decision.js +0 -39
- package/dist/runtime/hooks/permission_decision.js.map +0 -1
- package/dist/runtime/workflow_fsm.d.ts +0 -21
- package/dist/runtime/workflow_fsm.d.ts.map +0 -1
- package/dist/runtime/workflow_fsm.js +0 -25
- package/dist/runtime/workflow_fsm.js.map +0 -1
- package/dist/runtime/workflow_map.d.ts +0 -26
- package/dist/runtime/workflow_map.d.ts.map +0 -1
- package/dist/runtime/workflow_map.js +0 -38
- package/dist/runtime/workflow_map.js.map +0 -1
- package/dist/scope.d.ts +0 -48
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -111
- package/dist/scope.js.map +0 -1
- package/dist/setup/cli/topic_create_step.d.ts +0 -84
- package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
- package/dist/setup/cli/topic_create_step.js +0 -213
- package/dist/setup/cli/topic_create_step.js.map +0 -1
- package/dist/system-export.d.ts +0 -65
- package/dist/system-export.d.ts.map +0 -1
- package/dist/system-export.js +0 -194
- package/dist/system-export.js.map +0 -1
- package/dist/utterance/classifier.d.ts +0 -53
- package/dist/utterance/classifier.d.ts.map +0 -1
- package/dist/utterance/classifier.js +0 -184
- package/dist/utterance/classifier.js.map +0 -1
- package/dist/utterance/classifier.test.js +0 -147
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drift catalog — automated SessionEnd scan that surfaces this session's
|
|
3
|
-
* drift signals (0.7.22 / drift D10).
|
|
4
|
-
*
|
|
5
|
-
* D10 in the catalog: previously the agent only catalogued its drifts
|
|
6
|
-
* AFTER the user prompted "please put in all the drifting issues found
|
|
7
|
-
* recently." The whole project is anti-drift; the agent should be
|
|
8
|
-
* cataloguing its own drifts continuously as the dogfood proof.
|
|
9
|
-
*
|
|
10
|
-
* This module scans the session's JSONL transcript at SessionEnd for
|
|
11
|
-
* three classes of drift markers:
|
|
12
|
-
*
|
|
13
|
-
* 1. User-correction phrases in user messages ("you drifted",
|
|
14
|
-
* "stop X-ing", "no not that", "wrong")
|
|
15
|
-
* 2. Locked-rule citations in user OR assistant messages
|
|
16
|
-
* (feedback_*, mem-*, drift D\d+)
|
|
17
|
-
* 3. Agent mea-culpa phrases in assistant messages ("I should
|
|
18
|
-
* have", "my mistake", "I drifted", "I false-stopped")
|
|
19
|
-
*
|
|
20
|
-
* Hits are appended to `<dataRoot>/projects/<uuid>/drift-catalog.jsonl`.
|
|
21
|
-
* One JSON record per line: `{timestamp, session_id, kind, evidence}`.
|
|
22
|
-
*
|
|
23
|
-
* If the project UUID can't be resolved (no `.opensquid/project.json`
|
|
24
|
-
* card in any ancestor of cwd), entries fall back to
|
|
25
|
-
* `<dataRoot>/sessions/<session_id>/drift-catalog.jsonl` so the data
|
|
26
|
-
* isn't lost.
|
|
27
|
-
*
|
|
28
|
-
* Fail-open: any error (missing transcript, bad JSONL, write failure)
|
|
29
|
-
* is swallowed with a stderr warning. SessionEnd is cleanup, not
|
|
30
|
-
* blocking.
|
|
31
|
-
*/
|
|
32
|
-
import { promises as fs } from "node:fs";
|
|
33
|
-
import * as path from "node:path";
|
|
34
|
-
import { resolveDataRoot } from "../codex/store.js";
|
|
35
|
-
import { findProjectCard } from "../project.js";
|
|
36
|
-
import { readTranscriptLines } from "./transcript.js";
|
|
37
|
-
/**
|
|
38
|
-
* Locked-rule citation pattern. Matches:
|
|
39
|
-
* - `feedback_xxx` style memory file names
|
|
40
|
-
* - `mem-<hex>` style memory ids
|
|
41
|
-
* - `drift D1` through `drift D99` (the in-session catalog)
|
|
42
|
-
*/
|
|
43
|
-
const RULE_CITATION_REGEX = /\b(feedback_\w+|mem-[a-f0-9]+|drift\s+D\d+)\b/i;
|
|
44
|
-
/**
|
|
45
|
-
* User-correction phrases. Conservative on purpose — false-positives in
|
|
46
|
-
* the catalog are tolerable (it's a dogfood log, not user-facing) but
|
|
47
|
-
* we don't want to flood with every "wrong" in unrelated prose.
|
|
48
|
-
*/
|
|
49
|
-
const USER_CORRECTION_REGEX = /\b(you drifted|you're drifting|that'?s wrong|that'?s drift|stop (asking|doing|that|it)|don'?t (ask|do|repeat|forget)|no,? not that|you keep (drifting|doing))\b/i;
|
|
50
|
-
/**
|
|
51
|
-
* Agent mea-culpa phrases. Catches the patterns the agent uses when
|
|
52
|
-
* acknowledging it drifted — useful retroactive signal.
|
|
53
|
-
*/
|
|
54
|
-
const MEA_CULPA_REGEX = /\b(I should have|I drifted|sorry,? I (drifted|missed)|that was a drift|I false-?stopped|I false-?started|I keep drifting|my (mistake|drift))\b/i;
|
|
55
|
-
/**
|
|
56
|
-
* Scan a transcript and return all detected drift markers. Pure
|
|
57
|
-
* function; exported for direct testing.
|
|
58
|
-
*/
|
|
59
|
-
export function scanTranscriptForDrift(lines, sessionId, now = () => new Date()) {
|
|
60
|
-
const entries = [];
|
|
61
|
-
for (const line of lines) {
|
|
62
|
-
const event = safeParseLine(line);
|
|
63
|
-
if (!event)
|
|
64
|
-
continue;
|
|
65
|
-
if (event.type === "user") {
|
|
66
|
-
const text = extractUserText(event);
|
|
67
|
-
if (!text)
|
|
68
|
-
continue;
|
|
69
|
-
pushIfMatch(entries, text, USER_CORRECTION_REGEX, "user_correction", sessionId, now);
|
|
70
|
-
pushIfMatch(entries, text, RULE_CITATION_REGEX, "rule_citation", sessionId, now);
|
|
71
|
-
}
|
|
72
|
-
else if (event.type === "assistant") {
|
|
73
|
-
const text = extractAssistantText(event);
|
|
74
|
-
if (!text)
|
|
75
|
-
continue;
|
|
76
|
-
pushIfMatch(entries, text, MEA_CULPA_REGEX, "mea_culpa", sessionId, now);
|
|
77
|
-
pushIfMatch(entries, text, RULE_CITATION_REGEX, "rule_citation", sessionId, now);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return entries;
|
|
81
|
-
}
|
|
82
|
-
function pushIfMatch(entries, text, regex, kind, sessionId, now) {
|
|
83
|
-
const match = text.match(regex);
|
|
84
|
-
if (!match)
|
|
85
|
-
return;
|
|
86
|
-
const evidence = match[0].slice(0, 200);
|
|
87
|
-
const idx = text.indexOf(match[0]);
|
|
88
|
-
const start = Math.max(0, idx - 100);
|
|
89
|
-
const end = Math.min(text.length, idx + match[0].length + 100);
|
|
90
|
-
const context = text.slice(start, end);
|
|
91
|
-
entries.push({
|
|
92
|
-
timestamp: now().toISOString(),
|
|
93
|
-
session_id: sessionId,
|
|
94
|
-
kind,
|
|
95
|
-
evidence,
|
|
96
|
-
context,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Run the SessionEnd drift-catalog scan + persist results. Returns
|
|
101
|
-
* the number of entries written (0 if nothing matched OR write failed).
|
|
102
|
-
*/
|
|
103
|
-
export async function runDriftCatalogScan(input) {
|
|
104
|
-
if (!input.transcriptPath)
|
|
105
|
-
return 0;
|
|
106
|
-
let lines;
|
|
107
|
-
try {
|
|
108
|
-
lines = await readTranscriptLines(input.transcriptPath);
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
return 0;
|
|
112
|
-
}
|
|
113
|
-
const entries = scanTranscriptForDrift(lines, input.sessionId, input.now);
|
|
114
|
-
if (entries.length === 0)
|
|
115
|
-
return 0;
|
|
116
|
-
const targetPath = await resolveCatalogPath(input.cwd, input.sessionId, input.dataRoot);
|
|
117
|
-
try {
|
|
118
|
-
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
119
|
-
const serialized = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
120
|
-
await fs.appendFile(targetPath, serialized, "utf8");
|
|
121
|
-
return entries.length;
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return 0;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Decide where to write the catalog: project-scoped if we can resolve
|
|
129
|
-
* a project UUID from cwd; session-scoped fallback otherwise.
|
|
130
|
-
*
|
|
131
|
-
* Exported for testing.
|
|
132
|
-
*/
|
|
133
|
-
export async function resolveCatalogPath(cwd, sessionId, dataRoot) {
|
|
134
|
-
const root = resolveDataRoot(dataRoot);
|
|
135
|
-
if (cwd) {
|
|
136
|
-
try {
|
|
137
|
-
const found = await findProjectCard(cwd);
|
|
138
|
-
if (found) {
|
|
139
|
-
return path.join(root, "projects", found.card.uuid, "drift-catalog.jsonl");
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
// fall through to session-scoped fallback
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return path.join(root, "sessions", sessionId, "drift-catalog.jsonl");
|
|
147
|
-
}
|
|
148
|
-
// ---------------------------------------------------------------------
|
|
149
|
-
// Local duck-typed transcript parsing (mirrors transcript.ts patterns
|
|
150
|
-
// to avoid widening the public surface of that module).
|
|
151
|
-
// ---------------------------------------------------------------------
|
|
152
|
-
function safeParseLine(line) {
|
|
153
|
-
if (!line.trim())
|
|
154
|
-
return null;
|
|
155
|
-
try {
|
|
156
|
-
return JSON.parse(line);
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function extractUserText(event) {
|
|
163
|
-
const content = event.message?.content;
|
|
164
|
-
if (typeof content === "string")
|
|
165
|
-
return content;
|
|
166
|
-
return "";
|
|
167
|
-
}
|
|
168
|
-
function extractAssistantText(event) {
|
|
169
|
-
const content = event.message?.content;
|
|
170
|
-
if (typeof content === "string")
|
|
171
|
-
return content;
|
|
172
|
-
if (!Array.isArray(content))
|
|
173
|
-
return "";
|
|
174
|
-
const parts = [];
|
|
175
|
-
for (const block of content) {
|
|
176
|
-
if (block && typeof block === "object" && "type" in block && block.type === "text") {
|
|
177
|
-
const text = block.text;
|
|
178
|
-
if (typeof text === "string")
|
|
179
|
-
parts.push(text);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return parts.join("\n");
|
|
183
|
-
}
|
|
184
|
-
//# sourceMappingURL=drift-catalog.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"drift-catalog.js","sourceRoot":"","sources":["../../src.legacy/hooks/drift-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for drift-catalog (0.7.22 / drift D10).
|
|
3
|
-
*
|
|
4
|
-
* Covers scanTranscriptForDrift pure function. End-to-end persistence
|
|
5
|
-
* exercised in resolveCatalogPath tests (no project card) — full
|
|
6
|
-
* project-card flow needs fs fixtures and is left to integration.
|
|
7
|
-
*/
|
|
8
|
-
import { promises as fs } from "node:fs";
|
|
9
|
-
import * as os from "node:os";
|
|
10
|
-
import * as path from "node:path";
|
|
11
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
12
|
-
import { resolveCatalogPath, scanTranscriptForDrift } from "./drift-catalog.js";
|
|
13
|
-
const FIXED_NOW = () => new Date("2026-05-18T06:00:00.000Z");
|
|
14
|
-
const SESSION_ID = "test-session-abc";
|
|
15
|
-
function userLine(text) {
|
|
16
|
-
return JSON.stringify({ type: "user", message: { role: "user", content: text } });
|
|
17
|
-
}
|
|
18
|
-
function assistantLine(text) {
|
|
19
|
-
return JSON.stringify({
|
|
20
|
-
type: "assistant",
|
|
21
|
-
message: { role: "assistant", content: [{ type: "text", text }] },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
describe("scanTranscriptForDrift — user correction patterns", () => {
|
|
25
|
-
it("catches 'you drifted'", () => {
|
|
26
|
-
const entries = scanTranscriptForDrift([userLine("hey you drifted again")], SESSION_ID, FIXED_NOW);
|
|
27
|
-
expect(entries.length).toBeGreaterThanOrEqual(1);
|
|
28
|
-
expect(entries.some((e) => e.kind === "user_correction")).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
it("catches 'stop asking'", () => {
|
|
31
|
-
const entries = scanTranscriptForDrift([userLine("please stop asking me to confirm every step")], SESSION_ID, FIXED_NOW);
|
|
32
|
-
expect(entries.some((e) => e.kind === "user_correction")).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
it("catches 'don't repeat'", () => {
|
|
35
|
-
const entries = scanTranscriptForDrift([userLine("don't repeat the same false-stop pattern")], SESSION_ID, FIXED_NOW);
|
|
36
|
-
expect(entries.some((e) => e.kind === "user_correction")).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
it("does NOT catch 'wrong' on its own", () => {
|
|
39
|
-
const entries = scanTranscriptForDrift([userLine("the answer was wrong")], SESSION_ID, FIXED_NOW);
|
|
40
|
-
expect(entries.filter((e) => e.kind === "user_correction")).toEqual([]);
|
|
41
|
-
});
|
|
42
|
-
it("ignores tool_result-shaped user events (array content)", () => {
|
|
43
|
-
const line = JSON.stringify({
|
|
44
|
-
type: "user",
|
|
45
|
-
message: { role: "user", content: [{ type: "tool_result", content: "you drifted" }] },
|
|
46
|
-
});
|
|
47
|
-
const entries = scanTranscriptForDrift([line], SESSION_ID, FIXED_NOW);
|
|
48
|
-
expect(entries).toEqual([]);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
describe("scanTranscriptForDrift — rule citations", () => {
|
|
52
|
-
it("catches feedback_* references in user text", () => {
|
|
53
|
-
const entries = scanTranscriptForDrift([userLine("re-read feedback_full_automation_mode")], SESSION_ID, FIXED_NOW);
|
|
54
|
-
expect(entries.some((e) => e.kind === "rule_citation" && e.evidence.startsWith("feedback_"))).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
it("catches mem-<hex> references in assistant text", () => {
|
|
57
|
-
const entries = scanTranscriptForDrift([assistantLine("per mem-3cf66f39 we don't apologize")], SESSION_ID, FIXED_NOW);
|
|
58
|
-
expect(entries.some((e) => e.kind === "rule_citation" && e.evidence.startsWith("mem-"))).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
it("catches drift D-number references", () => {
|
|
61
|
-
const entries = scanTranscriptForDrift([userLine("you're hitting drift D9 again")], SESSION_ID, FIXED_NOW);
|
|
62
|
-
expect(entries.some((e) => e.kind === "rule_citation" && /drift\s+D9/i.test(e.evidence))).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe("scanTranscriptForDrift — mea-culpa patterns", () => {
|
|
66
|
-
it("catches 'I drifted' in assistant text", () => {
|
|
67
|
-
const entries = scanTranscriptForDrift([assistantLine("you're right, I drifted from the locked rule")], SESSION_ID, FIXED_NOW);
|
|
68
|
-
expect(entries.some((e) => e.kind === "mea_culpa")).toBe(true);
|
|
69
|
-
});
|
|
70
|
-
it("catches 'I should have' in assistant text", () => {
|
|
71
|
-
const entries = scanTranscriptForDrift([assistantLine("I should have called recall before answering")], SESSION_ID, FIXED_NOW);
|
|
72
|
-
expect(entries.some((e) => e.kind === "mea_culpa")).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
it("catches 'I false-stopped'", () => {
|
|
75
|
-
const entries = scanTranscriptForDrift([assistantLine("I false-stopped at the end of last turn")], SESSION_ID, FIXED_NOW);
|
|
76
|
-
expect(entries.some((e) => e.kind === "mea_culpa")).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
it("does NOT fire on assistant claim that's just a quote of the user", () => {
|
|
79
|
-
// The user said "I drifted" → assistant repeated it. Mea-culpa pattern still fires
|
|
80
|
-
// — accepted noise per the conservative-on-purpose stance. This test documents
|
|
81
|
-
// current behavior, not perfection.
|
|
82
|
-
const entries = scanTranscriptForDrift([assistantLine('the user said "I drifted" but that wasn\'t about me')], SESSION_ID, FIXED_NOW);
|
|
83
|
-
expect(entries.some((e) => e.kind === "mea_culpa")).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe("scanTranscriptForDrift — entry shape", () => {
|
|
87
|
-
it("populates timestamp, session_id, kind, evidence, context", () => {
|
|
88
|
-
const entries = scanTranscriptForDrift([userLine("you drifted again right after I locked the rule")], SESSION_ID, FIXED_NOW);
|
|
89
|
-
const correction = entries.find((e) => e.kind === "user_correction");
|
|
90
|
-
expect(correction).toBeDefined();
|
|
91
|
-
expect(correction.timestamp).toBe("2026-05-18T06:00:00.000Z");
|
|
92
|
-
expect(correction.session_id).toBe(SESSION_ID);
|
|
93
|
-
expect(correction.evidence).toBe("you drifted");
|
|
94
|
-
expect(correction.context).toContain("you drifted");
|
|
95
|
-
});
|
|
96
|
-
it("handles multiple turns and accumulates entries", () => {
|
|
97
|
-
const entries = scanTranscriptForDrift([
|
|
98
|
-
userLine("you drifted yesterday"),
|
|
99
|
-
assistantLine("I should have noticed sooner"),
|
|
100
|
-
userLine("feedback_full_automation_mode applies here"),
|
|
101
|
-
], SESSION_ID, FIXED_NOW);
|
|
102
|
-
expect(entries.length).toBeGreaterThanOrEqual(3);
|
|
103
|
-
const kinds = new Set(entries.map((e) => e.kind));
|
|
104
|
-
expect(kinds.has("user_correction")).toBe(true);
|
|
105
|
-
expect(kinds.has("mea_culpa")).toBe(true);
|
|
106
|
-
expect(kinds.has("rule_citation")).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
it("skips malformed JSONL lines silently", () => {
|
|
109
|
-
const entries = scanTranscriptForDrift(["not valid json", "", userLine("you drifted")], SESSION_ID, FIXED_NOW);
|
|
110
|
-
expect(entries.some((e) => e.kind === "user_correction")).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
describe("resolveCatalogPath — session fallback when no project card", () => {
|
|
114
|
-
let tmpDir;
|
|
115
|
-
beforeEach(async () => {
|
|
116
|
-
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "drift-catalog-test-"));
|
|
117
|
-
});
|
|
118
|
-
afterEach(async () => {
|
|
119
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
120
|
-
});
|
|
121
|
-
it("returns session-scoped path when no project card exists in cwd ancestry", async () => {
|
|
122
|
-
// Use a fresh tmpdir as both cwd and dataRoot — guaranteed no .opensquid/ card up the tree.
|
|
123
|
-
const noCardDir = await fs.mkdtemp(path.join(os.tmpdir(), "no-card-"));
|
|
124
|
-
try {
|
|
125
|
-
const got = await resolveCatalogPath(noCardDir, "sess-xyz", tmpDir);
|
|
126
|
-
expect(got).toBe(path.join(tmpDir, "sessions", "sess-xyz", "drift-catalog.jsonl"));
|
|
127
|
-
}
|
|
128
|
-
finally {
|
|
129
|
-
await fs.rm(noCardDir, { recursive: true, force: true });
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
it("returns session-scoped path when cwd is undefined", async () => {
|
|
133
|
-
const got = await resolveCatalogPath(undefined, "sess-xyz", tmpDir);
|
|
134
|
-
expect(got).toBe(path.join(tmpDir, "sessions", "sess-xyz", "drift-catalog.jsonl"));
|
|
135
|
-
});
|
|
136
|
-
it("returns project-scoped path when cwd has a .opensquid/project.json card", async () => {
|
|
137
|
-
// Create a project card in the cwd
|
|
138
|
-
const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), "with-card-"));
|
|
139
|
-
try {
|
|
140
|
-
await fs.mkdir(path.join(projectRoot, ".opensquid"), { recursive: true });
|
|
141
|
-
await fs.writeFile(path.join(projectRoot, ".opensquid", "project.json"), JSON.stringify({
|
|
142
|
-
version: 1,
|
|
143
|
-
id: "test-project",
|
|
144
|
-
uuid: "abc-uuid-123",
|
|
145
|
-
created_at: "2026-05-18T00:00:00.000Z",
|
|
146
|
-
}));
|
|
147
|
-
const got = await resolveCatalogPath(projectRoot, "sess-xyz", tmpDir);
|
|
148
|
-
expect(got).toBe(path.join(tmpDir, "projects", "abc-uuid-123", "drift-catalog.jsonl"));
|
|
149
|
-
}
|
|
150
|
-
finally {
|
|
151
|
-
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drift pattern catalog — known anti-patterns opensquid intercepts at
|
|
3
|
-
* the Claude Code PreToolUse hook before the agent commits the action.
|
|
4
|
-
*
|
|
5
|
-
* Each pattern has:
|
|
6
|
-
* - `id` — stable identifier for the rule
|
|
7
|
-
* - `trigger` — matcher against tool call input
|
|
8
|
-
* - `lesson` — short reference to the lesson that owns this rule
|
|
9
|
-
* - `message` — what the agent sees in stderr when intercepted
|
|
10
|
-
* - `severity` — "block" (exit 2 stops the call) or "warn" (stderr
|
|
11
|
-
* only, call proceeds)
|
|
12
|
-
*
|
|
13
|
-
* Patterns are CONSERVATIVE on purpose: we'd rather miss a drift than
|
|
14
|
-
* spam false positives. The catalog grows lesson-by-lesson as new
|
|
15
|
-
* drifts are observed and the user endorses the rule.
|
|
16
|
-
*/
|
|
17
|
-
export type DriftSeverity = "block" | "warn";
|
|
18
|
-
export interface DriftPattern {
|
|
19
|
-
id: string;
|
|
20
|
-
/** Tool name to match. "*" matches any tool; specific names match
|
|
21
|
-
* one exact tool. 0.7.24 / D2: broadened from a fixed union to
|
|
22
|
-
* `string` so MCP tool names like `mcp__plugin_telegram_telegram__reply`
|
|
23
|
-
* can be matched directly. */
|
|
24
|
-
tool: string;
|
|
25
|
-
/** Matcher applied to the relevant tool input field. */
|
|
26
|
-
trigger: DriftTrigger;
|
|
27
|
-
/** Lesson id (in the workflow codex) that owns this rule. */
|
|
28
|
-
lesson: string;
|
|
29
|
-
/** Short, agent-facing explanation surfaced when triggered. */
|
|
30
|
-
message: string;
|
|
31
|
-
severity: DriftSeverity;
|
|
32
|
-
}
|
|
33
|
-
export type DriftTrigger = {
|
|
34
|
-
kind: "bash_contains";
|
|
35
|
-
needle: string;
|
|
36
|
-
strip_quotes?: boolean;
|
|
37
|
-
} | {
|
|
38
|
-
kind: "bash_regex";
|
|
39
|
-
pattern: string;
|
|
40
|
-
strip_quotes?: boolean;
|
|
41
|
-
} | {
|
|
42
|
-
kind: "text_regex";
|
|
43
|
-
pattern: string;
|
|
44
|
-
field: string;
|
|
45
|
-
};
|
|
46
|
-
export declare const DRIFT_PATTERNS: DriftPattern[];
|
|
47
|
-
export interface ToolCallInput {
|
|
48
|
-
tool: string;
|
|
49
|
-
input: Record<string, unknown>;
|
|
50
|
-
}
|
|
51
|
-
export interface DriftHit {
|
|
52
|
-
pattern: DriftPattern;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Run the catalog against a tool call. Returns every matching pattern;
|
|
56
|
-
* caller decides block-vs-warn based on highest severity.
|
|
57
|
-
*/
|
|
58
|
-
export declare function findDrifts(call: ToolCallInput): DriftHit[];
|
|
59
|
-
/**
|
|
60
|
-
* Strip HEREDOC bodies (`<<DELIM ... DELIM` and variants) from a
|
|
61
|
-
* shell command.
|
|
62
|
-
*
|
|
63
|
-
* Recognizes:
|
|
64
|
-
* <<EOF ... \nEOF (unquoted delimiter, expansion-allowing)
|
|
65
|
-
* <<'EOF' ... \nEOF (single-quoted, literal body)
|
|
66
|
-
* <<"EOF" ... \nEOF (double-quoted, literal body)
|
|
67
|
-
* <<-EOF ... \nEOF (tab-stripping mode)
|
|
68
|
-
* <<-'EOF' ... \nEOF (combined)
|
|
69
|
-
*
|
|
70
|
-
* Delimiter is any word-char sequence (EOF, END, HERE, MARKER, etc.).
|
|
71
|
-
* Lazy `[\s\S]*?` matches across newlines; `\b` after the backref
|
|
72
|
-
* ensures `EOFX` doesn't close an `<<EOF` block.
|
|
73
|
-
*
|
|
74
|
-
* If a HEREDOC has no closing delimiter (truncated input), regex
|
|
75
|
-
* doesn't match and the body stays intact — fail-open behavior.
|
|
76
|
-
*
|
|
77
|
-
* Exported for direct unit testing.
|
|
78
|
-
*/
|
|
79
|
-
export declare function stripHeredocBodies(s: string): string;
|
|
80
|
-
/**
|
|
81
|
-
* Decide the final exit code + message from a list of hits.
|
|
82
|
-
*
|
|
83
|
-
* - Any "block" hit → exit 2 with all blocking messages
|
|
84
|
-
* - Only "warn" hits → exit 0, print warnings to stderr
|
|
85
|
-
* - No hits → exit 0 silently
|
|
86
|
-
*
|
|
87
|
-
* Emergency bypass: `OPENSQUID_SKIP_DRIFT=1` downgrades every block to
|
|
88
|
-
* an audit-trail warning (exit 0, stderr explains the bypass). Two ways
|
|
89
|
-
* to set it:
|
|
90
|
-
*
|
|
91
|
-
* 1. Parent process env — useful for whole-session bypass (e.g. set
|
|
92
|
-
* before launching Claude Code).
|
|
93
|
-
* 2. Inline command prefix — e.g. `OPENSQUID_SKIP_DRIFT=1 git push`.
|
|
94
|
-
* The hook reads the COMMAND STRING from the Bash tool input and
|
|
95
|
-
* sees the prefix even though the env var never reaches the hook's
|
|
96
|
-
* own process.env (the hook is a sibling subprocess spawned by
|
|
97
|
-
* Claude Code, not a child of the would-be Bash subprocess).
|
|
98
|
-
*
|
|
99
|
-
* Matches the shape of the version-gate (`OPENSQUID_SKIP_VERSION_GATE=1`)
|
|
100
|
-
* and workflow-gate (`OPENSQUID_SKIP_WORKFLOW_GATE=1`) bypasses so the
|
|
101
|
-
* operator only has one mental model — except those two only check
|
|
102
|
-
* process.env (their hooks happen before any command runs); drift-
|
|
103
|
-
* patterns additionally checks the command-string prefix so the bypass
|
|
104
|
-
* can be requested per-command from within an existing session.
|
|
105
|
-
*/
|
|
106
|
-
export declare function decide(hits: DriftHit[], call?: ToolCallInput): {
|
|
107
|
-
exit: 0 | 2;
|
|
108
|
-
stderr: string;
|
|
109
|
-
};
|
|
110
|
-
//# sourceMappingURL=drift-patterns.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"drift-patterns.d.ts","sourceRoot":"","sources":["../../src.legacy/hooks/drift-patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX;;;kCAG8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,OAAO,EAAE,YAAY,CAAC;IACtB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAM3D,eAAO,MAAM,cAAc,EAAE,YAAY,EA4GxC,CAAC;AAMF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,QAAQ,EAAE,CAS1D;AA4DD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAOpD;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,QAAQ,EAAE,EAChB,IAAI,CAAC,EAAE,aAAa,GACnB;IACD,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAsBA"}
|