@possumtech/rummy 2.1.0 → 2.2.1
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/.env.example +40 -15
- package/.xai.key +1 -0
- package/PLUGINS.md +169 -53
- package/README.md +38 -32
- package/SPEC.md +366 -179
- package/bin/digest.js +1097 -0
- package/biome/no-fallbacks.grit +2 -2
- package/gemini.key +1 -0
- package/lang/en.json +10 -1
- package/migrations/001_initial_schema.sql +9 -2
- package/package.json +19 -8
- package/service.js +1 -0
- package/src/agent/AgentLoop.js +76 -26
- package/src/agent/ContextAssembler.js +2 -0
- package/src/agent/Entries.js +238 -60
- package/src/agent/ProjectAgent.js +44 -0
- package/src/agent/TurnExecutor.js +99 -30
- package/src/agent/XmlParser.js +206 -111
- package/src/agent/errors.js +35 -0
- package/src/agent/known_queries.sql +1 -1
- package/src/agent/known_store.sql +3 -42
- package/src/agent/materializeContext.js +30 -1
- package/src/agent/runs.sql +8 -18
- package/src/agent/tokens.js +0 -1
- package/src/agent/turns.sql +1 -0
- package/src/hooks/Hooks.js +26 -0
- package/src/hooks/RummyContext.js +12 -1
- package/src/lib/hedberg/README.md +60 -0
- package/src/lib/hedberg/hedberg.js +60 -0
- package/src/lib/hedberg/marker.js +158 -0
- package/src/{plugins → lib}/hedberg/matcher.js +1 -2
- package/src/llm/LlmProvider.js +41 -3
- package/src/llm/openaiStream.js +17 -0
- package/src/plugins/ask_user/ask_user.js +12 -2
- package/src/plugins/ask_user/ask_userDoc.md +1 -5
- package/src/plugins/budget/README.md +29 -24
- package/src/plugins/budget/budget.js +166 -110
- package/src/plugins/cli/README.md +3 -4
- package/src/plugins/cli/cli.js +31 -5
- package/src/plugins/cloudflare/cloudflare.js +136 -0
- package/src/plugins/cp/cp.js +41 -4
- package/src/plugins/cp/cpDoc.md +5 -6
- package/src/plugins/engine/engine.sql +1 -1
- package/src/plugins/env/README.md +5 -4
- package/src/plugins/env/env.js +7 -4
- package/src/plugins/env/envDoc.md +7 -8
- package/src/plugins/error/error.js +56 -15
- package/src/plugins/file/README.md +12 -3
- package/src/plugins/file/file.js +2 -2
- package/src/plugins/get/get.js +59 -36
- package/src/plugins/get/getDoc.md +10 -34
- package/src/plugins/google/google.js +115 -0
- package/src/plugins/hedberg/hedberg.js +13 -56
- package/src/plugins/helpers.js +66 -12
- package/src/plugins/index.js +1 -2
- package/src/plugins/instructions/README.md +44 -47
- package/src/plugins/instructions/instructions-system.md +44 -0
- package/src/plugins/instructions/instructions-user.md +53 -0
- package/src/plugins/instructions/instructions.js +58 -189
- package/src/plugins/known/README.md +6 -7
- package/src/plugins/known/known.js +24 -30
- package/src/plugins/log/log.js +41 -32
- package/src/plugins/mv/mv.js +40 -1
- package/src/plugins/mv/mvDoc.md +1 -8
- package/src/plugins/ollama/ollama.js +4 -3
- package/src/plugins/openai/openai.js +4 -3
- package/src/plugins/openrouter/openrouter.js +14 -4
- package/src/plugins/persona/README.md +11 -13
- package/src/plugins/persona/default.md +29 -0
- package/src/plugins/persona/persona.js +10 -66
- package/src/plugins/policy/policy.js +23 -22
- package/src/plugins/prompt/README.md +37 -27
- package/src/plugins/prompt/prompt.js +13 -19
- package/src/plugins/rm/rm.js +18 -0
- package/src/plugins/rm/rmDoc.md +5 -6
- package/src/plugins/rpc/rpc.js +3 -3
- package/src/plugins/set/set.js +205 -323
- package/src/plugins/set/setDoc.md +47 -17
- package/src/plugins/sh/README.md +6 -5
- package/src/plugins/sh/sh.js +8 -5
- package/src/plugins/sh/shDoc.md +7 -8
- package/src/plugins/skill/README.md +37 -14
- package/src/plugins/skill/skill.js +200 -101
- package/src/plugins/skill/skillDoc.js +3 -0
- package/src/plugins/skill/skillDoc.md +9 -0
- package/src/plugins/stream/README.md +7 -6
- package/src/plugins/stream/finalize.js +100 -0
- package/src/plugins/stream/stream.js +13 -45
- package/src/plugins/telemetry/telemetry.js +27 -4
- package/src/plugins/think/think.js +2 -3
- package/src/plugins/think/thinkDoc.md +2 -4
- package/src/plugins/unknown/README.md +1 -1
- package/src/plugins/unknown/unknown.js +17 -19
- package/src/plugins/update/update.js +4 -51
- package/src/plugins/update/updateDoc.md +21 -6
- package/src/plugins/xai/xai.js +68 -102
- package/src/plugins/yolo/yolo.js +102 -75
- package/src/sql/functions/hedmatch.js +1 -1
- package/src/sql/functions/hedreplace.js +1 -1
- package/src/sql/functions/hedsearch.js +1 -1
- package/src/sql/functions/slugify.js +16 -2
- package/BENCH_ENVIRONMENT.md +0 -230
- package/CLIENT_INTERFACE.md +0 -396
- package/last_run.txt +0 -5617
- package/scriptify/ask_run.js +0 -77
- package/scriptify/cache_probe.js +0 -66
- package/scriptify/cache_probe_grok.js +0 -74
- package/src/agent/budget.js +0 -33
- package/src/agent/config.js +0 -38
- package/src/plugins/hedberg/README.md +0 -71
- package/src/plugins/hedberg/docs.md +0 -0
- package/src/plugins/hedberg/edits.js +0 -55
- package/src/plugins/hedberg/normalize.js +0 -17
- package/src/plugins/hedberg/sed.js +0 -49
- package/src/plugins/instructions/instructions.md +0 -34
- package/src/plugins/instructions/instructions_104.md +0 -8
- package/src/plugins/instructions/instructions_105.md +0 -39
- package/src/plugins/instructions/instructions_106.md +0 -22
- package/src/plugins/instructions/instructions_107.md +0 -17
- package/src/plugins/instructions/instructions_108.md +0 -0
- package/src/plugins/known/knownDoc.js +0 -3
- package/src/plugins/known/knownDoc.md +0 -8
- package/src/plugins/unknown/unknownDoc.js +0 -3
- package/src/plugins/unknown/unknownDoc.md +0 -11
- package/turns/cli_1777462658211/turn_001.txt +0 -772
- package/turns/cli_1777462658211/turn_002.txt +0 -606
- package/turns/cli_1777462658211/turn_003.txt +0 -667
- package/turns/cli_1777462658211/turn_004.txt +0 -297
- package/turns/cli_1777462658211/turn_005.txt +0 -301
- package/turns/cli_1777462658211/turn_006.txt +0 -262
- package/turns/cli_1777465095132/turn_001.txt +0 -715
- package/turns/cli_1777465095132/turn_002.txt +0 -236
- package/turns/cli_1777465095132/turn_003.txt +0 -287
- package/turns/cli_1777465095132/turn_004.txt +0 -694
- package/turns/cli_1777465095132/turn_005.txt +0 -422
- package/turns/cli_1777465095132/turn_006.txt +0 -365
- package/turns/cli_1777465095132/turn_007.txt +0 -885
- package/turns/cli_1777465095132/turn_008.txt +0 -1277
- package/turns/cli_1777465095132/turn_009.txt +0 -736
- /package/src/{plugins → lib}/hedberg/patterns.js +0 -0
package/biome/no-fallbacks.grit
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// No silent fallbacks outside hedberg.
|
|
1
|
+
// No silent fallbacks outside src/lib/hedberg/ + src/agent/XmlParser.js.
|
|
2
2
|
// Rule: interiors crash on contract violation, boundaries validate.
|
|
3
3
|
// Patterns like `|| 0`, `?? ""` silently mask missing data.
|
|
4
4
|
// hedberg is the stochastic-interpretation boundary — fallbacks
|
|
@@ -39,7 +39,7 @@ or {
|
|
|
39
39
|
`Number.parseFloat(process.env.$_) || $_`,
|
|
40
40
|
`Number.parseFloat(process.env.$_) ?? $_`
|
|
41
41
|
} as $match where {
|
|
42
|
-
$filename <: not includes "src/
|
|
42
|
+
$filename <: not includes "src/lib/hedberg/",
|
|
43
43
|
$filename <: not includes "src/agent/XmlParser.js",
|
|
44
44
|
$filename <: not includes "/test/",
|
|
45
45
|
$filename <: not includes ".test.js",
|
package/gemini.key
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AIzaSyD4VBYwfo5wiVp0IIy368rlbEhJTnL2k2c
|
package/lang/en.json
CHANGED
|
@@ -30,5 +30,14 @@
|
|
|
30
30
|
"error.xai_base_url_missing": "xai/ model requested but XAI_BASE_URL is not set.",
|
|
31
31
|
"error.xai_api_key_missing": "xai/ model requested but XAI_API_KEY is not set.",
|
|
32
32
|
"error.xai_auth": "xAI Authentication Error: {status}. Please check your XAI_API_KEY.",
|
|
33
|
-
"error.xai_api": "xAI API error: {status}"
|
|
33
|
+
"error.xai_api": "xAI API error: {status}",
|
|
34
|
+
"error.google_auth": "Google AI Studio authentication error: {status}. Check the gemini.key file in repo root.",
|
|
35
|
+
"error.google_api": "Google AI Studio API error: {status}",
|
|
36
|
+
"error.google_models_failed": "Google AI Studio /v1beta/models/{model} failed: {status}",
|
|
37
|
+
"error.google_no_context_length": "Google AI Studio model '{model}' returned no inputTokenLimit.",
|
|
38
|
+
"error.cloudflare_auth": "Cloudflare Workers AI authentication error: {status}. Check cloudflare.key and CLOUDFLARE_ACCOUNT_ID.",
|
|
39
|
+
"error.cloudflare_api": "Cloudflare Workers AI API error: {status}",
|
|
40
|
+
"error.cloudflare_models_failed": "Cloudflare /ai/models/search for '{model}' failed: {status}",
|
|
41
|
+
"error.cloudflare_model_not_found": "Cloudflare Workers AI model '{model}' not found in models-search results.",
|
|
42
|
+
"error.cloudflare_no_context_length": "Cloudflare Workers AI model '{model}' returned no context_window or max_input_tokens property."
|
|
34
43
|
}
|
|
@@ -99,6 +99,13 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
99
99
|
, reasoning_tokens INTEGER NOT NULL DEFAULT 0 CHECK (reasoning_tokens >= 0)
|
|
100
100
|
, total_tokens INTEGER NOT NULL DEFAULT 0 CHECK (total_tokens >= 0)
|
|
101
101
|
, cost REAL NOT NULL DEFAULT 0 CHECK (cost >= 0)
|
|
102
|
+
-- Full response metadata from the provider — everything except
|
|
103
|
+
-- content/reasoning_content (those live on the assistant://N entry
|
|
104
|
+
-- and turns.reasoning_content respectively). Catches finish_reason,
|
|
105
|
+
-- system_fingerprint, response id, service_tier, raw usage object,
|
|
106
|
+
-- and whatever provider-specific fields land on the response. Kept
|
|
107
|
+
-- as opaque JSON so provider shape drift doesn't bite us.
|
|
108
|
+
, response_metadata JSON NOT NULL DEFAULT '{}' CHECK (json_valid(response_metadata))
|
|
102
109
|
, created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
103
110
|
);
|
|
104
111
|
CREATE INDEX IF NOT EXISTS idx_turns_run_seq ON turns (run_id, sequence);
|
|
@@ -109,7 +116,7 @@ CREATE TABLE IF NOT EXISTS file_constraints (
|
|
|
109
116
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
110
117
|
, project_id INTEGER NOT NULL REFERENCES projects (id) ON DELETE CASCADE
|
|
111
118
|
, pattern TEXT NOT NULL
|
|
112
|
-
, visibility TEXT NOT NULL CHECK (visibility IN ('
|
|
119
|
+
, visibility TEXT NOT NULL CHECK (visibility IN ('add', 'readonly', 'ignore'))
|
|
113
120
|
, created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
114
121
|
, UNIQUE (project_id, pattern)
|
|
115
122
|
);
|
|
@@ -125,7 +132,7 @@ CREATE TABLE IF NOT EXISTS entries (
|
|
|
125
132
|
, scope TEXT NOT NULL
|
|
126
133
|
, path TEXT NOT NULL CHECK (length(path) <= 2048)
|
|
127
134
|
, scheme TEXT GENERATED ALWAYS AS (schemeOf(path)) STORED
|
|
128
|
-
, body TEXT NOT NULL DEFAULT ''
|
|
135
|
+
, body TEXT NOT NULL DEFAULT '' CHECK (length(body) <= $entry_size_max)
|
|
129
136
|
, attributes JSON NOT NULL DEFAULT '{}' CHECK (json_valid(attributes))
|
|
130
137
|
, hash TEXT
|
|
131
138
|
, created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@possumtech/rummy",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Relational Unknowns Memory Management Yoke",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm"
|
|
@@ -40,24 +40,34 @@
|
|
|
40
40
|
"test:all": "npm run lint && npm run test:unit && npm run test:intg && npm run test:e2e",
|
|
41
41
|
"test:unit": "node --env-file-if-exists=.env.example --env-file-if-exists=.env.test --experimental-test-coverage --test-coverage-lines=50 --test-coverage-branches=50 --test-coverage-functions=50 --test-concurrency=1 --test-force-exit --test $(find src -name '*.test.js')",
|
|
42
42
|
"test:intg": "node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test --test-concurrency=1 --test-force-exit --test $(find test/integration -name '*.test.js')",
|
|
43
|
-
"test:e2e": "mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test --test-concurrency=1 --test-force-exit --test-reporter=spec --test $(find test/e2e -name
|
|
44
|
-
"test:live": "mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test --test-concurrency=1 --test-force-exit --test-reporter=spec --test $(find test/live -name
|
|
43
|
+
"test:e2e": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test --test-concurrency=1 --test-force-exit --test-reporter=spec --test $(find test/e2e -name \"*.test.js\") 2>&1 | tee /tmp/rummy_test_diag/e2e_$(date +%Y%m%dT%H%M%S).log; status=$?; node bin/digest.js /tmp/rummy_test_diag/ 2>&1 | tail -3; exit $status'",
|
|
44
|
+
"test:live": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test --test-concurrency=1 --test-force-exit --test-reporter=spec --test $(find test/live -name \"*.test.js\") 2>&1 | tee /tmp/rummy_test_diag/live_$(date +%Y%m%dT%H%M%S).log'",
|
|
45
45
|
"test:clean": "rm -rf test/lme/results test/swe/results test/swe/repos test/tmp /tmp/rummy_test_diag /tmp/rummy_test_*.db /tmp/rummy_test_*.db-shm /tmp/rummy_test_*.db-wal && echo 'Test artifacts cleaned.'",
|
|
46
46
|
"test:lme:get": "node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test test/lme/download.js",
|
|
47
|
-
"test:lme": "bash -c 'mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test test/lme/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/lme_$(date +%Y%m%dT%H%M%S).log' --",
|
|
47
|
+
"test:lme": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test test/lme/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/lme_$(date +%Y%m%dT%H%M%S).log' --",
|
|
48
48
|
"test:swe:setup": "bash test/swe/setup.sh",
|
|
49
49
|
"test:swe:get": "node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test test/swe/download.js",
|
|
50
|
-
"test:swe": "bash -c 'mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test test/swe/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/swe_$(date +%Y%m%dT%H%M%S).log' --",
|
|
50
|
+
"test:swe": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.test test/swe/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/swe_$(date +%Y%m%dT%H%M%S).log' --",
|
|
51
51
|
"test:swe:eval": "bash -c 'cd test/swe && source .venv/bin/activate && python evaluate.py \"$@\"' --",
|
|
52
52
|
"test:swe:baseline": "bash -c 'cd test/swe && source .venv/bin/activate && python baseline.py \"$@\"' --",
|
|
53
53
|
"test:lme:clean": "rm -rf test/lme/results/*/",
|
|
54
54
|
"test:swe:clean": "rm -rf test/swe/results/*/ test/swe/repos/",
|
|
55
55
|
"test:tbench:setup": "bash -c 'set -a; source .env.tbench; set +a; bash test/tbench/setup.sh'",
|
|
56
|
-
"test:tbench": "bash -c '
|
|
56
|
+
"test:tbench": "bash -c 'echo \"Specify a profile: test:tbench:xfast | :gemma | :xfast_or\" >&2 && exit 64'",
|
|
57
|
+
"test:tbench:xfast": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.tbench --env-file-if-exists=.env.tbench.xfast test/tbench/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/tbench_xfast_$(date +%Y%m%dT%H%M%S).log' --",
|
|
58
|
+
"test:tbench:gemma": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.tbench --env-file-if-exists=.env.tbench.gemma test/tbench/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/tbench_gemma_$(date +%Y%m%dT%H%M%S).log' --",
|
|
59
|
+
"test:tbench:xfast_or": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.tbench --env-file-if-exists=.env.tbench.xfast_or test/tbench/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/tbench_xfast_or_$(date +%Y%m%dT%H%M%S).log' --",
|
|
60
|
+
"test:tbench:g43": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.tbench --env-file-if-exists=.env.tbench.g43 test/tbench/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/tbench_g43_$(date +%Y%m%dT%H%M%S).log' --",
|
|
57
61
|
"test:tbench:clean": "rm -rf test/tbench/results/*/",
|
|
62
|
+
"test:tbench:summary": "node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.tbench test/tbench/summarize.js",
|
|
63
|
+
"test:programbench:setup": "bash test/programbench/setup.sh",
|
|
64
|
+
"test:programbench": "bash -c 'set -o pipefail; mkdir -p /tmp/rummy_test_diag && node --env-file-if-exists=.env.example --env-file-if-exists=.env --env-file-if-exists=.env.tbench --env-file-if-exists=.env.tbench.gemma test/programbench/runner.js \"$@\" 2>&1 | tee /tmp/rummy_test_diag/programbench_$(date +%Y%m%dT%H%M%S).log' --",
|
|
65
|
+
"test:programbench:eval": "bash -c 'cd test/programbench && . .venv/bin/activate && programbench eval \"$@\"' --",
|
|
66
|
+
"test:programbench:clean": "rm -rf test/programbench/results/*/",
|
|
58
67
|
"test:clear": "rm -rf /tmp/rummy_test_diag /tmp/rummy_test_*.db /tmp/rummy_test_*.db-shm /tmp/rummy_test_*.db-wal /tmp/rummy-stories-*",
|
|
59
68
|
"test:demo": "node --env-file-if-exists=.env.example --env-file-if-exists=.env bin/demo.js",
|
|
60
|
-
"test:spec": "node test/spec-coverage.js"
|
|
69
|
+
"test:spec": "node test/spec-coverage.js",
|
|
70
|
+
"dev:digest": "bash -c 'DB=\"${1:-rummy_dev.db}\"; NAME=$(basename \"$DB\"); TMP=$(mktemp -d); sqlite3 \"$DB\" \".backup $TMP/$NAME\" && node bin/digest.js \"$TMP/$NAME\"; rm -rf \"$TMP\"' --"
|
|
61
71
|
},
|
|
62
72
|
"devDependencies": {
|
|
63
73
|
"@biomejs/biome": "^2.4.6"
|
|
@@ -69,6 +79,7 @@
|
|
|
69
79
|
"htmlparser2": "^12.0.0",
|
|
70
80
|
"picomatch": "^4.0.4",
|
|
71
81
|
"ws": "^8.19.0",
|
|
72
|
-
"xpath": "^0.0.34"
|
|
82
|
+
"xpath": "^0.0.34",
|
|
83
|
+
"yauzl-promise": "^4.0.0"
|
|
73
84
|
}
|
|
74
85
|
}
|
package/service.js
CHANGED
package/src/agent/AgentLoop.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
1
4
|
import msg from "./messages.js";
|
|
2
5
|
|
|
6
|
+
const DEFAULT_PERSONA_PATH = join(
|
|
7
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
8
|
+
"../plugins/persona/default.md",
|
|
9
|
+
);
|
|
10
|
+
let cachedDefaultPersona = null;
|
|
11
|
+
async function loadDefaultPersona() {
|
|
12
|
+
if (cachedDefaultPersona == null) {
|
|
13
|
+
cachedDefaultPersona = await readFile(DEFAULT_PERSONA_PATH, "utf8");
|
|
14
|
+
}
|
|
15
|
+
return cachedDefaultPersona;
|
|
16
|
+
}
|
|
17
|
+
|
|
3
18
|
const HTTP_TO_RUN_STATE = {
|
|
4
19
|
100: "proposed",
|
|
5
20
|
102: "streaming",
|
|
@@ -23,6 +38,11 @@ export default class AgentLoop {
|
|
|
23
38
|
this.#hooks = hooks;
|
|
24
39
|
this.#turnExecutor = turnExecutor;
|
|
25
40
|
this.#entries = entries;
|
|
41
|
+
hooks.run.wake.on(this.#onWake.bind(this));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async #onWake({ runAlias, body, mode }) {
|
|
45
|
+
await this.inject(runAlias, body, mode);
|
|
26
46
|
}
|
|
27
47
|
|
|
28
48
|
abort(runId) {
|
|
@@ -52,6 +72,7 @@ export default class AgentLoop {
|
|
|
52
72
|
const s = await this.#db.get_run_summary.get({ id: runId });
|
|
53
73
|
await hook.completed.emit({
|
|
54
74
|
projectId,
|
|
75
|
+
runId,
|
|
55
76
|
...out,
|
|
56
77
|
model: s.model,
|
|
57
78
|
turns: s.turns,
|
|
@@ -113,10 +134,12 @@ export default class AgentLoop {
|
|
|
113
134
|
const {
|
|
114
135
|
fork: isFork = false,
|
|
115
136
|
temperature = null,
|
|
116
|
-
persona = null,
|
|
137
|
+
persona: personaOpt = null,
|
|
117
138
|
contextLimit = null,
|
|
118
139
|
} = options;
|
|
119
140
|
const requestedModel = model;
|
|
141
|
+
let persona = personaOpt;
|
|
142
|
+
if (!persona) persona = await loadDefaultPersona();
|
|
120
143
|
|
|
121
144
|
if (run && isFork) {
|
|
122
145
|
const existingRun = await this.#db.get_run_by_alias.get({ alias: run });
|
|
@@ -133,6 +156,12 @@ export default class AgentLoop {
|
|
|
133
156
|
context_limit: contextLimit,
|
|
134
157
|
});
|
|
135
158
|
await this.#entries.forkEntries(existingRun.id, runRow.id);
|
|
159
|
+
// Absolute turn numbering across the lineage; SPEC
|
|
160
|
+
// §budget_enforcement. Without this, the fork's first
|
|
161
|
+
// dispatch lands at turn 1 while inherited run_views carry
|
|
162
|
+
// parent-side turn values, and the budget grinder's
|
|
163
|
+
// `current_turn − 1` rule sees nothing meaningful.
|
|
164
|
+
await this.#entries.setNextTurn(runRow.id, existingRun.next_turn);
|
|
136
165
|
await this.#writeRunEntry(runRow.id, alias, prompt, {
|
|
137
166
|
projectId,
|
|
138
167
|
parentRunId: existingRun.id,
|
|
@@ -468,14 +497,17 @@ export default class AgentLoop {
|
|
|
468
497
|
`[LOOP] ${currentAlias} iter=${loopIteration} turn done: status=${result.status} turn=${result.turn}`,
|
|
469
498
|
);
|
|
470
499
|
|
|
471
|
-
const verdict = await this.#hooks.
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
500
|
+
const verdict = await this.#hooks.turn.verdict.filter(
|
|
501
|
+
{ continue: true },
|
|
502
|
+
{
|
|
503
|
+
store: this.#entries,
|
|
504
|
+
runId: currentRunId,
|
|
505
|
+
loopId: currentLoopId,
|
|
506
|
+
turn: result.turn,
|
|
507
|
+
recorded: result.recorded,
|
|
508
|
+
summaryText: result.summaryText,
|
|
509
|
+
},
|
|
510
|
+
);
|
|
479
511
|
const vStatus = verdict.status === undefined ? "-" : verdict.status;
|
|
480
512
|
const vReason = verdict.reason ? verdict.reason : "-";
|
|
481
513
|
console.error(
|
|
@@ -661,6 +693,40 @@ export default class AgentLoop {
|
|
|
661
693
|
|
|
662
694
|
const nextTurn = runRow.next_turn;
|
|
663
695
|
|
|
696
|
+
// Resolve the owning loop_id BEFORE writing the prompt entry so
|
|
697
|
+
// it lands with correct loop scope. Active run → reuse the
|
|
698
|
+
// running loop; otherwise enqueue the next loop and write the
|
|
699
|
+
// prompt with the new loop's id.
|
|
700
|
+
let loopId;
|
|
701
|
+
if (this.#activeRuns.has(runRow.id)) {
|
|
702
|
+
// Active runs have exactly one loop at status=102 by the
|
|
703
|
+
// loops table invariant — trust the contract.
|
|
704
|
+
const currentLoop = await this.#db.get_current_loop.get({
|
|
705
|
+
run_id: runRow.id,
|
|
706
|
+
});
|
|
707
|
+
loopId = currentLoop.id;
|
|
708
|
+
} else {
|
|
709
|
+
const injectLoopSeq = await this.#db.next_loop.get({
|
|
710
|
+
run_id: runRow.id,
|
|
711
|
+
});
|
|
712
|
+
const enqueued = await this.#db.enqueue_loop.get({
|
|
713
|
+
run_id: runRow.id,
|
|
714
|
+
sequence: injectLoopSeq.sequence,
|
|
715
|
+
mode,
|
|
716
|
+
model: runRow.model,
|
|
717
|
+
prompt: message,
|
|
718
|
+
config: JSON.stringify({
|
|
719
|
+
noRepo,
|
|
720
|
+
noInteraction,
|
|
721
|
+
noWeb,
|
|
722
|
+
noProposals,
|
|
723
|
+
yolo,
|
|
724
|
+
temperature: options?.temperature,
|
|
725
|
+
}),
|
|
726
|
+
});
|
|
727
|
+
loopId = enqueued.id;
|
|
728
|
+
}
|
|
729
|
+
|
|
664
730
|
await this.#entries.set({
|
|
665
731
|
runId: runRow.id,
|
|
666
732
|
turn: nextTurn,
|
|
@@ -668,6 +734,7 @@ export default class AgentLoop {
|
|
|
668
734
|
body: message,
|
|
669
735
|
state: "resolved",
|
|
670
736
|
attributes: { mode },
|
|
737
|
+
loopId,
|
|
671
738
|
writer: "plugin",
|
|
672
739
|
});
|
|
673
740
|
|
|
@@ -675,23 +742,6 @@ export default class AgentLoop {
|
|
|
675
742
|
return { run: runAlias, status: runRow.status, injected: "next_turn" };
|
|
676
743
|
}
|
|
677
744
|
|
|
678
|
-
const injectLoopSeq = await this.#db.next_loop.get({ run_id: runRow.id });
|
|
679
|
-
await this.#db.enqueue_loop.get({
|
|
680
|
-
run_id: runRow.id,
|
|
681
|
-
sequence: injectLoopSeq.sequence,
|
|
682
|
-
mode,
|
|
683
|
-
model: runRow.model,
|
|
684
|
-
prompt: message,
|
|
685
|
-
config: JSON.stringify({
|
|
686
|
-
noRepo,
|
|
687
|
-
noInteraction,
|
|
688
|
-
noWeb,
|
|
689
|
-
noProposals,
|
|
690
|
-
yolo,
|
|
691
|
-
temperature: options?.temperature,
|
|
692
|
-
}),
|
|
693
|
-
});
|
|
694
|
-
|
|
695
745
|
const projectId = runRow.project_id;
|
|
696
746
|
const project = await this.#db.get_project_by_id.get({ id: projectId });
|
|
697
747
|
const controller = new AbortController();
|
|
@@ -9,6 +9,7 @@ export default class ContextAssembler {
|
|
|
9
9
|
toolSet = null,
|
|
10
10
|
lastContextTokens = 0,
|
|
11
11
|
turn = 1,
|
|
12
|
+
persona = "",
|
|
12
13
|
} = {},
|
|
13
14
|
hooks,
|
|
14
15
|
) {
|
|
@@ -27,6 +28,7 @@ export default class ContextAssembler {
|
|
|
27
28
|
lastContextTokens,
|
|
28
29
|
toolSet,
|
|
29
30
|
turn,
|
|
31
|
+
persona,
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
const system = await hooks.assembly.system.filter(systemPrompt, ctx);
|