@oh-my-pi/pi-coding-agent 15.7.2 → 15.7.3
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/CHANGELOG.md +75 -6
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/extension-flags.d.ts +36 -0
- package/dist/types/config/config-file.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +23 -0
- package/dist/types/config/keybindings.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +88 -65
- package/dist/types/edit/hashline/diff.d.ts +3 -3
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
- package/dist/types/eval/agent-bridge.d.ts +25 -0
- package/dist/types/eval/backend.d.ts +17 -2
- package/dist/types/eval/budget-bridge.d.ts +29 -0
- package/dist/types/eval/idle-timeout.d.ts +28 -0
- package/dist/types/eval/js/executor.d.ts +8 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +13 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
- package/dist/types/extensibility/plugins/manager.d.ts +12 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/memory-backend/index.d.ts +1 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +3 -3
- package/dist/types/mnemopi/backend.d.ts +4 -0
- package/dist/types/mnemopi/config.d.ts +29 -0
- package/dist/types/mnemopi/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -2
- package/dist/types/modes/components/omfg-panel.d.ts +19 -0
- package/dist/types/modes/controllers/command-controller.d.ts +7 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
- package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
- package/dist/types/modes/gradient-highlight.d.ts +5 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -3
- package/dist/types/modes/magic-keywords.d.ts +14 -0
- package/dist/types/modes/markdown-prose.d.ts +27 -0
- package/dist/types/modes/orchestrate.d.ts +7 -2
- package/dist/types/modes/shared.d.ts +1 -1
- package/dist/types/modes/turn-budget.d.ts +18 -0
- package/dist/types/modes/types.d.ts +7 -3
- package/dist/types/modes/ultrathink.d.ts +7 -2
- package/dist/types/modes/workflow.d.ts +15 -0
- package/dist/types/sdk.d.ts +13 -3
- package/dist/types/session/agent-session.d.ts +40 -17
- package/dist/types/session/session-manager.d.ts +18 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/session/shake-types.d.ts +24 -0
- package/dist/types/task/executor.d.ts +2 -2
- package/dist/types/tiny/models.d.ts +15 -1
- package/dist/types/tiny/title-protocol.d.ts +4 -0
- package/dist/types/tools/index.d.ts +19 -3
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/package.json +10 -10
- package/src/autoresearch/tools/run-experiment.ts +45 -113
- package/src/cli/args.ts +39 -16
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/plugin-cli.ts +11 -2
- package/src/config/config-file.ts +98 -13
- package/src/config/file-lock.ts +60 -17
- package/src/config/keybindings.ts +78 -27
- package/src/config/model-registry.ts +7 -1
- package/src/config/settings-schema.ts +94 -67
- package/src/config/settings.ts +12 -0
- package/src/edit/hashline/diff.ts +81 -24
- package/src/edit/renderer.ts +16 -12
- package/src/eval/__tests__/agent-bridge.test.ts +433 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/idle-timeout.test.ts +66 -0
- package/src/eval/__tests__/shared-executors.test.ts +21 -0
- package/src/eval/agent-bridge.ts +295 -0
- package/src/eval/backend.ts +17 -2
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/idle-timeout.ts +80 -0
- package/src/eval/js/executor.ts +35 -7
- package/src/eval/js/index.ts +2 -1
- package/src/eval/js/shared/prelude.txt +85 -1
- package/src/eval/js/tool-bridge.ts +9 -0
- package/src/eval/py/executor.ts +41 -14
- package/src/eval/py/index.ts +2 -1
- package/src/eval/py/prelude.py +132 -1
- package/src/exec/bash-executor.ts +2 -3
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/runner.ts +12 -2
- package/src/extensibility/plugins/git-url.ts +90 -4
- package/src/extensibility/plugins/manager.ts +103 -7
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +88 -88
- package/src/main.ts +44 -55
- package/src/memory-backend/index.ts +1 -1
- package/src/memory-backend/resolve.ts +3 -3
- package/src/memory-backend/types.ts +3 -3
- package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
- package/src/{mnemosyne → mnemopi}/config.ts +36 -36
- package/src/{mnemosyne → mnemopi}/state.ts +67 -67
- package/src/modes/components/agent-dashboard.ts +6 -6
- package/src/modes/components/custom-editor.ts +4 -11
- package/src/modes/components/extensions/state-manager.ts +3 -4
- package/src/modes/components/footer.ts +8 -9
- package/src/modes/components/hook-selector.ts +86 -20
- package/src/modes/components/oauth-selector.ts +93 -21
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/settings-defs.ts +2 -2
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +38 -19
- package/src/modes/components/tree-selector.ts +4 -3
- package/src/modes/components/user-message-selector.ts +94 -19
- package/src/modes/components/user-message.ts +8 -1
- package/src/modes/controllers/command-controller.ts +57 -0
- package/src/modes/controllers/event-controller.ts +60 -2
- package/src/modes/controllers/input-controller.ts +14 -11
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/gradient-highlight.ts +23 -6
- package/src/modes/interactive-mode.ts +41 -7
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/orchestrate.ts +17 -11
- package/src/modes/shared.ts +3 -11
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/ultrathink.ts +16 -10
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/workflow.ts +42 -0
- package/src/prompts/system/omfg-user.md +51 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/eval.md +13 -1
- package/src/prompts/tools/memory-edit.md +1 -1
- package/src/sdk.ts +63 -33
- package/src/session/agent-session.ts +373 -56
- package/src/session/session-manager.ts +32 -0
- package/src/session/session-storage.ts +68 -8
- package/src/session/shake-types.ts +44 -0
- package/src/slash-commands/builtin-registry.ts +41 -16
- package/src/task/executor.ts +3 -3
- package/src/task/index.ts +6 -6
- package/src/tiny/models.ts +30 -2
- package/src/tiny/title-protocol.ts +11 -1
- package/src/tiny/worker.ts +19 -7
- package/src/tools/eval.ts +202 -26
- package/src/tools/grouped-file-output.ts +9 -2
- package/src/tools/index.ts +17 -5
- package/src/tools/memory-edit.ts +4 -4
- package/src/tools/memory-recall.ts +5 -5
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +4 -4
- package/src/tools/render-utils.ts +2 -1
- package/src/tools/search.ts +480 -76
- package/dist/types/mnemosyne/backend.d.ts +0 -4
- package/dist/types/mnemosyne/config.d.ts +0 -29
- package/dist/types/mnemosyne/state.d.ts +0 -72
- /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
- /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
|
@@ -851,6 +851,12 @@ export class ModelRegistry {
|
|
|
851
851
|
|
|
852
852
|
/**
|
|
853
853
|
* @param authStorage - Auth storage for API key resolution
|
|
854
|
+
*
|
|
855
|
+
* Sync constructor — eagerly loads bundled + cached models so tests and
|
|
856
|
+
* synchronous callers see a fully-populated registry immediately. Production
|
|
857
|
+
* boot paths SHOULD prefer {@link ModelRegistry.create} so the YAML/JSONC
|
|
858
|
+
* migration step lands off the event loop's hot path before the first
|
|
859
|
+
* `tryLoad()` runs.
|
|
854
860
|
*/
|
|
855
861
|
constructor(
|
|
856
862
|
readonly authStorage: AuthStorage,
|
|
@@ -866,7 +872,7 @@ export class ModelRegistry {
|
|
|
866
872
|
}
|
|
867
873
|
return undefined;
|
|
868
874
|
});
|
|
869
|
-
// Load models synchronously in constructor
|
|
875
|
+
// Load models synchronously in constructor.
|
|
870
876
|
this.#loadModels();
|
|
871
877
|
}
|
|
872
878
|
|
|
@@ -14,9 +14,12 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
AUTO_THINKING_MODEL_OPTIONS,
|
|
16
16
|
AUTO_THINKING_MODEL_VALUES,
|
|
17
|
+
DEFAULT_SHAKE_SUMMARY_MODEL_KEY,
|
|
17
18
|
ONLINE_AUTO_THINKING_MODEL_KEY,
|
|
18
19
|
ONLINE_MEMORY_MODEL_KEY,
|
|
19
20
|
ONLINE_TINY_TITLE_MODEL_KEY,
|
|
21
|
+
SHAKE_SUMMARY_MODEL_OPTIONS,
|
|
22
|
+
SHAKE_SUMMARY_MODEL_VALUES,
|
|
20
23
|
TINY_MEMORY_MODEL_OPTIONS,
|
|
21
24
|
TINY_MEMORY_MODEL_VALUES,
|
|
22
25
|
TINY_TITLE_MODEL_OPTIONS,
|
|
@@ -1136,12 +1139,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
1136
1139
|
|
|
1137
1140
|
"compaction.strategy": {
|
|
1138
1141
|
type: "enum",
|
|
1139
|
-
values: ["context-full", "handoff", "off"] as const,
|
|
1142
|
+
values: ["context-full", "handoff", "shake", "shake-summary", "off"] as const,
|
|
1140
1143
|
default: "context-full",
|
|
1141
1144
|
ui: {
|
|
1142
1145
|
tab: "context",
|
|
1143
1146
|
label: "Compaction Strategy",
|
|
1144
|
-
description:
|
|
1147
|
+
description:
|
|
1148
|
+
"Choose in-place context-full maintenance, auto-handoff, surgical shake (drop heavy content), shake with local-model summaries, or disable auto maintenance (off)",
|
|
1145
1149
|
options: [
|
|
1146
1150
|
{
|
|
1147
1151
|
value: "context-full",
|
|
@@ -1149,6 +1153,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
1149
1153
|
description: "Summarize in-place and keep the current session",
|
|
1150
1154
|
},
|
|
1151
1155
|
{ value: "handoff", label: "Handoff", description: "Generate handoff and continue in a new session" },
|
|
1156
|
+
{
|
|
1157
|
+
value: "shake",
|
|
1158
|
+
label: "Shake",
|
|
1159
|
+
description: "Drop heavy content (tool results + large blocks) in place; recover via artifact",
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
value: "shake-summary",
|
|
1163
|
+
label: "Shake (summary)",
|
|
1164
|
+
description: "Shake, but compress heavy regions with a local on-device model instead of dropping",
|
|
1165
|
+
},
|
|
1152
1166
|
{
|
|
1153
1167
|
value: "off",
|
|
1154
1168
|
label: "Off",
|
|
@@ -1327,70 +1341,70 @@ export const SETTINGS_SCHEMA = {
|
|
|
1327
1341
|
"memories.summaryInjectionTokenLimit": { type: "number", default: 5000 },
|
|
1328
1342
|
|
|
1329
1343
|
// Memory backend selector — picks between local memories pipeline,
|
|
1330
|
-
//
|
|
1344
|
+
// Mnemopi local SQLite, Hindsight remote memory, or off. Legacy
|
|
1331
1345
|
// `memories.enabled` keeps gating the local backend; see config/settings.ts
|
|
1332
1346
|
// migration for details.
|
|
1333
1347
|
"memory.backend": {
|
|
1334
1348
|
type: "enum",
|
|
1335
|
-
values: ["off", "local", "hindsight", "
|
|
1349
|
+
values: ["off", "local", "hindsight", "mnemopi"] as const,
|
|
1336
1350
|
default: "off",
|
|
1337
1351
|
ui: {
|
|
1338
1352
|
tab: "memory",
|
|
1339
1353
|
label: "Memory Backend",
|
|
1340
|
-
description: "Off, local summary pipeline,
|
|
1354
|
+
description: "Off, local summary pipeline, Mnemopi SQLite, or Hindsight remote memory",
|
|
1341
1355
|
options: [
|
|
1342
1356
|
{ value: "off", label: "Off", description: "No memory subsystem runs" },
|
|
1343
1357
|
{ value: "local", label: "Local", description: "Local rollout summarisation pipeline (memory_summary.md)" },
|
|
1344
1358
|
{ value: "hindsight", label: "Hindsight", description: "Vectorize Hindsight remote memory service" },
|
|
1345
1359
|
{
|
|
1346
|
-
value: "
|
|
1347
|
-
label: "
|
|
1360
|
+
value: "mnemopi",
|
|
1361
|
+
label: "Mnemopi",
|
|
1348
1362
|
description: "Local SQLite recall/retain backend with optional embeddings",
|
|
1349
1363
|
},
|
|
1350
1364
|
],
|
|
1351
1365
|
},
|
|
1352
1366
|
},
|
|
1353
1367
|
|
|
1354
|
-
//
|
|
1355
|
-
"
|
|
1368
|
+
// Mnemopi local SQLite memory backend.
|
|
1369
|
+
"mnemopi.dbPath": {
|
|
1356
1370
|
type: "string",
|
|
1357
1371
|
default: undefined,
|
|
1358
1372
|
ui: {
|
|
1359
1373
|
tab: "memory",
|
|
1360
|
-
label: "
|
|
1374
|
+
label: "Mnemopi DB Path",
|
|
1361
1375
|
description: "Optional SQLite DB path. Defaults to the agent memories directory.",
|
|
1362
|
-
condition: "
|
|
1376
|
+
condition: "mnemopiActive",
|
|
1363
1377
|
},
|
|
1364
1378
|
},
|
|
1365
|
-
"
|
|
1379
|
+
"mnemopi.bank": {
|
|
1366
1380
|
type: "string",
|
|
1367
1381
|
default: undefined,
|
|
1368
1382
|
ui: {
|
|
1369
1383
|
tab: "memory",
|
|
1370
|
-
label: "
|
|
1384
|
+
label: "Mnemopi Bank",
|
|
1371
1385
|
description: "Optional shared bank base name. Per-project modes derive project-local banks from it.",
|
|
1372
|
-
condition: "
|
|
1386
|
+
condition: "mnemopiActive",
|
|
1373
1387
|
},
|
|
1374
1388
|
},
|
|
1375
|
-
"
|
|
1389
|
+
"mnemopi.scoping": {
|
|
1376
1390
|
type: "enum",
|
|
1377
1391
|
values: ["global", "per-project", "per-project-tagged"] as const,
|
|
1378
1392
|
default: "per-project",
|
|
1379
1393
|
ui: {
|
|
1380
1394
|
tab: "memory",
|
|
1381
|
-
label: "
|
|
1395
|
+
label: "Mnemopi Scoping",
|
|
1382
1396
|
description:
|
|
1383
1397
|
"global = one shared bank; per-project = isolated bank per cwd; per-project-tagged = project-local writes plus global recall visibility",
|
|
1384
1398
|
options: [
|
|
1385
1399
|
{
|
|
1386
1400
|
value: "global",
|
|
1387
1401
|
label: "Global",
|
|
1388
|
-
description: "One shared
|
|
1402
|
+
description: "One shared Mnemopi bank for every project",
|
|
1389
1403
|
},
|
|
1390
1404
|
{
|
|
1391
1405
|
value: "per-project",
|
|
1392
1406
|
label: "Per project",
|
|
1393
|
-
description: "Project-local
|
|
1407
|
+
description: "Project-local Mnemopi bank per cwd basename",
|
|
1394
1408
|
},
|
|
1395
1409
|
{
|
|
1396
1410
|
value: "per-project-tagged",
|
|
@@ -1398,121 +1412,121 @@ export const SETTINGS_SCHEMA = {
|
|
|
1398
1412
|
description: "Write to a project-local bank but merge project + shared recall results",
|
|
1399
1413
|
},
|
|
1400
1414
|
],
|
|
1401
|
-
condition: "
|
|
1415
|
+
condition: "mnemopiActive",
|
|
1402
1416
|
},
|
|
1403
1417
|
},
|
|
1404
|
-
"
|
|
1418
|
+
"mnemopi.autoRecall": {
|
|
1405
1419
|
type: "boolean",
|
|
1406
1420
|
default: true,
|
|
1407
1421
|
ui: {
|
|
1408
1422
|
tab: "memory",
|
|
1409
|
-
label: "
|
|
1423
|
+
label: "Mnemopi Auto Recall",
|
|
1410
1424
|
description: "Recall local memories into the first turn of each session",
|
|
1411
|
-
condition: "
|
|
1425
|
+
condition: "mnemopiActive",
|
|
1412
1426
|
},
|
|
1413
1427
|
},
|
|
1414
|
-
"
|
|
1428
|
+
"mnemopi.autoRetain": {
|
|
1415
1429
|
type: "boolean",
|
|
1416
1430
|
default: true,
|
|
1417
1431
|
ui: {
|
|
1418
1432
|
tab: "memory",
|
|
1419
|
-
label: "
|
|
1420
|
-
description: "Retain completed conversation turns into local
|
|
1421
|
-
condition: "
|
|
1433
|
+
label: "Mnemopi Auto Retain",
|
|
1434
|
+
description: "Retain completed conversation turns into local Mnemopi memory",
|
|
1435
|
+
condition: "mnemopiActive",
|
|
1422
1436
|
},
|
|
1423
1437
|
},
|
|
1424
|
-
"
|
|
1438
|
+
"mnemopi.noEmbeddings": {
|
|
1425
1439
|
type: "boolean",
|
|
1426
1440
|
default: false,
|
|
1427
1441
|
ui: {
|
|
1428
1442
|
tab: "memory",
|
|
1429
|
-
label: "
|
|
1443
|
+
label: "Mnemopi Disable Embeddings",
|
|
1430
1444
|
description: "Force deterministic FTS-only recall instead of vector embeddings",
|
|
1431
|
-
condition: "
|
|
1445
|
+
condition: "mnemopiActive",
|
|
1432
1446
|
},
|
|
1433
1447
|
},
|
|
1434
|
-
"
|
|
1448
|
+
"mnemopi.embeddingModel": {
|
|
1435
1449
|
type: "string",
|
|
1436
1450
|
default: undefined,
|
|
1437
1451
|
ui: {
|
|
1438
1452
|
tab: "memory",
|
|
1439
|
-
label: "
|
|
1440
|
-
description: "Optional embedding model override passed to
|
|
1441
|
-
condition: "
|
|
1453
|
+
label: "Mnemopi Embedding Model",
|
|
1454
|
+
description: "Optional embedding model override passed to Mnemopi",
|
|
1455
|
+
condition: "mnemopiActive",
|
|
1442
1456
|
},
|
|
1443
1457
|
},
|
|
1444
|
-
"
|
|
1458
|
+
"mnemopi.embeddingApiUrl": {
|
|
1445
1459
|
type: "string",
|
|
1446
1460
|
default: undefined,
|
|
1447
1461
|
ui: {
|
|
1448
1462
|
tab: "memory",
|
|
1449
|
-
label: "
|
|
1450
|
-
description: "Optional OpenAI-compatible embedding endpoint passed to
|
|
1451
|
-
condition: "
|
|
1463
|
+
label: "Mnemopi Embedding API URL",
|
|
1464
|
+
description: "Optional OpenAI-compatible embedding endpoint passed to Mnemopi",
|
|
1465
|
+
condition: "mnemopiActive",
|
|
1452
1466
|
},
|
|
1453
1467
|
},
|
|
1454
|
-
"
|
|
1468
|
+
"mnemopi.embeddingApiKey": {
|
|
1455
1469
|
type: "string",
|
|
1456
1470
|
default: undefined,
|
|
1457
1471
|
ui: {
|
|
1458
1472
|
tab: "memory",
|
|
1459
|
-
label: "
|
|
1460
|
-
description: "Optional embedding API key passed to
|
|
1461
|
-
condition: "
|
|
1473
|
+
label: "Mnemopi Embedding API Key",
|
|
1474
|
+
description: "Optional embedding API key passed to Mnemopi",
|
|
1475
|
+
condition: "mnemopiActive",
|
|
1462
1476
|
},
|
|
1463
1477
|
},
|
|
1464
|
-
"
|
|
1478
|
+
"mnemopi.llmMode": {
|
|
1465
1479
|
type: "enum",
|
|
1466
1480
|
values: ["none", "smol", "remote"] as const,
|
|
1467
1481
|
default: "smol",
|
|
1468
1482
|
ui: {
|
|
1469
1483
|
tab: "memory",
|
|
1470
|
-
label: "
|
|
1484
|
+
label: "Mnemopi LLM Mode",
|
|
1471
1485
|
description: "Use no LLM, the configured smol model, or a remote OpenAI-compatible endpoint",
|
|
1472
|
-
condition: "
|
|
1486
|
+
condition: "mnemopiActive",
|
|
1473
1487
|
options: [
|
|
1474
|
-
{ value: "none", label: "None", description: "Disable
|
|
1488
|
+
{ value: "none", label: "None", description: "Disable Mnemopi LLM-backed extraction" },
|
|
1475
1489
|
{ value: "smol", label: "Smol", description: "Use the configured pi-ai smol model" },
|
|
1476
|
-
{ value: "remote", label: "Remote", description: "Use the
|
|
1490
|
+
{ value: "remote", label: "Remote", description: "Use the Mnemopi remote LLM settings below" },
|
|
1477
1491
|
],
|
|
1478
1492
|
},
|
|
1479
1493
|
},
|
|
1480
|
-
"
|
|
1494
|
+
"mnemopi.llmBaseUrl": {
|
|
1481
1495
|
type: "string",
|
|
1482
1496
|
default: undefined,
|
|
1483
1497
|
ui: {
|
|
1484
1498
|
tab: "memory",
|
|
1485
|
-
label: "
|
|
1486
|
-
description: "Optional OpenAI-compatible LLM endpoint for
|
|
1487
|
-
condition: "
|
|
1499
|
+
label: "Mnemopi LLM Base URL",
|
|
1500
|
+
description: "Optional OpenAI-compatible LLM endpoint for Mnemopi remote mode",
|
|
1501
|
+
condition: "mnemopiActive",
|
|
1488
1502
|
},
|
|
1489
1503
|
},
|
|
1490
|
-
"
|
|
1504
|
+
"mnemopi.llmApiKey": {
|
|
1491
1505
|
type: "string",
|
|
1492
1506
|
default: undefined,
|
|
1493
1507
|
ui: {
|
|
1494
1508
|
tab: "memory",
|
|
1495
|
-
label: "
|
|
1496
|
-
description: "Optional LLM API key for
|
|
1497
|
-
condition: "
|
|
1509
|
+
label: "Mnemopi LLM API Key",
|
|
1510
|
+
description: "Optional LLM API key for Mnemopi remote mode",
|
|
1511
|
+
condition: "mnemopiActive",
|
|
1498
1512
|
},
|
|
1499
1513
|
},
|
|
1500
|
-
"
|
|
1514
|
+
"mnemopi.llmModel": {
|
|
1501
1515
|
type: "string",
|
|
1502
1516
|
default: undefined,
|
|
1503
1517
|
ui: {
|
|
1504
1518
|
tab: "memory",
|
|
1505
|
-
label: "
|
|
1506
|
-
description: "Optional LLM model name for
|
|
1507
|
-
condition: "
|
|
1519
|
+
label: "Mnemopi LLM Model",
|
|
1520
|
+
description: "Optional LLM model name for Mnemopi remote mode",
|
|
1521
|
+
condition: "mnemopiActive",
|
|
1508
1522
|
},
|
|
1509
1523
|
},
|
|
1510
|
-
"
|
|
1511
|
-
"
|
|
1512
|
-
"
|
|
1513
|
-
"
|
|
1514
|
-
"
|
|
1515
|
-
"
|
|
1524
|
+
"mnemopi.retainEveryNTurns": { type: "number", default: 4 },
|
|
1525
|
+
"mnemopi.recallLimit": { type: "number", default: 8 },
|
|
1526
|
+
"mnemopi.recallContextTurns": { type: "number", default: 3 },
|
|
1527
|
+
"mnemopi.recallMaxQueryChars": { type: "number", default: 4000 },
|
|
1528
|
+
"mnemopi.injectionTokenLimit": { type: "number", default: 5000 },
|
|
1529
|
+
"mnemopi.debug": { type: "boolean", default: false },
|
|
1516
1530
|
|
|
1517
1531
|
// Hindsight (https://hindsight.vectorize.io)
|
|
1518
1532
|
"hindsight.apiUrl": {
|
|
@@ -2984,8 +2998,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
2984
2998
|
tab: "memory",
|
|
2985
2999
|
label: "Memory Model",
|
|
2986
3000
|
description:
|
|
2987
|
-
"
|
|
2988
|
-
condition: "
|
|
3001
|
+
"Mnemopi LLM for fact extraction + consolidation: online (smol/remote) by default, or a local on-device model",
|
|
3002
|
+
condition: "mnemopiActive",
|
|
2989
3003
|
options: TINY_MEMORY_MODEL_OPTIONS,
|
|
2990
3004
|
},
|
|
2991
3005
|
},
|
|
@@ -3004,6 +3018,19 @@ export const SETTINGS_SCHEMA = {
|
|
|
3004
3018
|
},
|
|
3005
3019
|
},
|
|
3006
3020
|
|
|
3021
|
+
"providers.shakeSummaryModel": {
|
|
3022
|
+
type: "enum",
|
|
3023
|
+
values: SHAKE_SUMMARY_MODEL_VALUES,
|
|
3024
|
+
default: DEFAULT_SHAKE_SUMMARY_MODEL_KEY,
|
|
3025
|
+
ui: {
|
|
3026
|
+
tab: "context",
|
|
3027
|
+
label: "Shake Summary Model",
|
|
3028
|
+
description:
|
|
3029
|
+
"Local on-device model used by /shake summary and the shake-summary compaction strategy to compress heavy regions. Runs entirely on-device; downloads on first use. Falls back to plain elide when unavailable.",
|
|
3030
|
+
options: SHAKE_SUMMARY_MODEL_OPTIONS,
|
|
3031
|
+
},
|
|
3032
|
+
},
|
|
3033
|
+
|
|
3007
3034
|
"providers.kimiApiFormat": {
|
|
3008
3035
|
type: "enum",
|
|
3009
3036
|
values: ["openai", "anthropic"] as const,
|
|
@@ -3292,7 +3319,7 @@ export type TreeFilterMode = SettingValue<"treeFilterMode">;
|
|
|
3292
3319
|
|
|
3293
3320
|
export interface CompactionSettings {
|
|
3294
3321
|
enabled: boolean;
|
|
3295
|
-
strategy: "context-full" | "handoff" | "off";
|
|
3322
|
+
strategy: "context-full" | "handoff" | "shake" | "shake-summary" | "off";
|
|
3296
3323
|
thresholdPercent: number;
|
|
3297
3324
|
thresholdTokens: number;
|
|
3298
3325
|
reserveTokens: number;
|
package/src/config/settings.ts
CHANGED
|
@@ -692,6 +692,18 @@ export class Settings {
|
|
|
692
692
|
raw.memory = memoryRoot;
|
|
693
693
|
}
|
|
694
694
|
|
|
695
|
+
// Rename the legacy local `mnemosyne` memory backend to `mnemopi`.
|
|
696
|
+
// - `memory.backend: "mnemosyne"` now selects the renamed backend.
|
|
697
|
+
// - the top-level `mnemosyne` settings object becomes `mnemopi`.
|
|
698
|
+
// Idempotent: skips the object move once `mnemopi` is materialised.
|
|
699
|
+
if (memoryBackendObj && memoryBackendObj.backend === "mnemosyne") {
|
|
700
|
+
memoryBackendObj.backend = "mnemopi";
|
|
701
|
+
}
|
|
702
|
+
if ("mnemosyne" in raw && !("mnemopi" in raw)) {
|
|
703
|
+
raw.mnemopi = raw.mnemosyne;
|
|
704
|
+
delete raw.mnemosyne;
|
|
705
|
+
}
|
|
706
|
+
|
|
695
707
|
// hindsight: dynamicBankId/agentName -> scoping enum + bankId
|
|
696
708
|
// - dynamicBankId=true → scoping="per-project" (closest semantic match;
|
|
697
709
|
// the legacy `agent::project::channel::user` tuple was per-project in
|
|
@@ -5,17 +5,25 @@
|
|
|
5
5
|
* pair to {@link generateDiffString} so the renderer can show the diff
|
|
6
6
|
* while the tool call is still streaming.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* and
|
|
8
|
+
* Uses the same snapshot-tag semantics as the apply path: a live content-hash
|
|
9
|
+
* match is accepted even when the tag was minted by a source that did not keep
|
|
10
|
+
* history, and stale tags recover through the session snapshot store when possible.
|
|
11
11
|
*/
|
|
12
12
|
import {
|
|
13
|
+
type ApplyResult,
|
|
14
|
+
applyEdits,
|
|
15
|
+
computeFileHash,
|
|
16
|
+
type Edit,
|
|
13
17
|
Patch as HashlinePatch,
|
|
18
|
+
hasBlockEdit,
|
|
19
|
+
MismatchError,
|
|
14
20
|
missingSnapshotTagMessage,
|
|
15
21
|
normalizeToLF,
|
|
16
22
|
type Patch,
|
|
17
23
|
type PatchSection,
|
|
18
|
-
|
|
24
|
+
parsePatchStreaming,
|
|
25
|
+
Recovery,
|
|
26
|
+
resolveBlockEdits,
|
|
19
27
|
type SnapshotStore,
|
|
20
28
|
stripBom,
|
|
21
29
|
} from "@oh-my-pi/hashline";
|
|
@@ -48,24 +56,79 @@ async function readSectionText(absolutePath: string, sectionPath: string): Promi
|
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
function
|
|
52
|
-
return
|
|
59
|
+
function hasAnchorScopedEdit(edits: readonly Edit[]): boolean {
|
|
60
|
+
return edits.some(edit => {
|
|
61
|
+
if (edit.kind === "delete") return true;
|
|
62
|
+
if (edit.kind === "block") return true;
|
|
63
|
+
return edit.cursor.kind === "before_anchor" || edit.cursor.kind === "after_anchor";
|
|
64
|
+
});
|
|
53
65
|
}
|
|
54
|
-
|
|
66
|
+
|
|
67
|
+
function createMismatchError(
|
|
55
68
|
section: PatchSection,
|
|
56
69
|
absolutePath: string,
|
|
57
|
-
|
|
70
|
+
normalized: string,
|
|
58
71
|
snapshots: SnapshotStore,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
expected: string,
|
|
73
|
+
): MismatchError {
|
|
74
|
+
return new MismatchError({
|
|
75
|
+
path: section.path,
|
|
76
|
+
expectedFileHash: expected,
|
|
77
|
+
actualFileHash: computeFileHash(normalized),
|
|
78
|
+
fileLines: normalized.split("\n"),
|
|
79
|
+
anchorLines: section.collectAnchorLines(),
|
|
80
|
+
hashRecognized: snapshots.byHash(absolutePath, expected) !== null,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parsePreviewEdits(section: PatchSection, streaming: boolean | undefined): readonly Edit[] {
|
|
85
|
+
return streaming ? parsePatchStreaming(section.diff).edits : section.edits;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function resolvePreviewEdits(args: {
|
|
89
|
+
section: PatchSection;
|
|
90
|
+
absolutePath: string;
|
|
91
|
+
normalized: string;
|
|
92
|
+
snapshots: SnapshotStore;
|
|
93
|
+
expected: string | undefined;
|
|
94
|
+
liveMatches: boolean;
|
|
95
|
+
edits: readonly Edit[];
|
|
96
|
+
}): readonly Edit[] {
|
|
97
|
+
const { section, absolutePath, normalized, snapshots, expected, liveMatches, edits } = args;
|
|
98
|
+
if (!hasBlockEdit(edits)) return edits;
|
|
99
|
+
const baseText = expected === undefined || liveMatches ? normalized : snapshots.byHash(absolutePath, expected)?.text;
|
|
100
|
+
if (baseText === undefined) {
|
|
101
|
+
throw createMismatchError(section, absolutePath, normalized, snapshots, expected ?? "");
|
|
65
102
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
103
|
+
return resolveBlockEdits(edits, baseText, section.path, nativeBlockResolver, { onUnresolved: "throw" });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function applyPreviewEdits(args: {
|
|
107
|
+
section: PatchSection;
|
|
108
|
+
absolutePath: string;
|
|
109
|
+
normalized: string;
|
|
110
|
+
snapshots: SnapshotStore;
|
|
111
|
+
options: HashlineDiffOptions;
|
|
112
|
+
}): ApplyResult {
|
|
113
|
+
const { section, absolutePath, normalized, snapshots, options } = args;
|
|
114
|
+
const expected = section.fileHash;
|
|
115
|
+
if (!options.skipHashValidation && expected === undefined) {
|
|
116
|
+
throw new Error(missingSnapshotTagMessage(section.path));
|
|
117
|
+
}
|
|
118
|
+
const liveMatches = expected !== undefined && computeFileHash(normalized) === expected;
|
|
119
|
+
const edits = parsePreviewEdits(section, options.streaming);
|
|
120
|
+
const resolved = resolvePreviewEdits({ section, absolutePath, normalized, snapshots, expected, liveMatches, edits });
|
|
121
|
+
if (options.skipHashValidation || expected === undefined || liveMatches) return applyEdits(normalized, resolved);
|
|
122
|
+
if (!hasAnchorScopedEdit(resolved)) return applyEdits(normalized, resolved);
|
|
123
|
+
|
|
124
|
+
const recovered = new Recovery(snapshots).tryRecover({
|
|
125
|
+
path: absolutePath,
|
|
126
|
+
currentText: normalized,
|
|
127
|
+
fileHash: expected,
|
|
128
|
+
edits: resolved,
|
|
129
|
+
});
|
|
130
|
+
if (recovered) return recovered;
|
|
131
|
+
throw createMismatchError(section, absolutePath, normalized, snapshots, expected);
|
|
69
132
|
}
|
|
70
133
|
|
|
71
134
|
export async function computeHashlineSectionDiff(
|
|
@@ -79,13 +142,7 @@ export async function computeHashlineSectionDiff(
|
|
|
79
142
|
const rawContent = await readSectionText(absolutePath, section.path);
|
|
80
143
|
const { text: content } = stripBom(rawContent);
|
|
81
144
|
const normalized = normalizeToLF(content);
|
|
82
|
-
|
|
83
|
-
const hashError = validateSectionHash(section, absolutePath, normalized, snapshots);
|
|
84
|
-
if (hashError) return { error: hashError };
|
|
85
|
-
}
|
|
86
|
-
const result = options.streaming
|
|
87
|
-
? section.applyPartialTo(normalized, nativeBlockResolver)
|
|
88
|
-
: section.applyTo(normalized, nativeBlockResolver);
|
|
145
|
+
const result = applyPreviewEdits({ section, absolutePath, normalized, snapshots, options });
|
|
89
146
|
if (normalized === result.text) return { error: `No changes would be made to ${section.path}.` };
|
|
90
147
|
return generateDiffString(normalized, result.text);
|
|
91
148
|
} catch (err) {
|
package/src/edit/renderer.ts
CHANGED
|
@@ -235,23 +235,27 @@ function renderPlainTextPreview(text: string, uiTheme: Theme, filePath?: string)
|
|
|
235
235
|
|
|
236
236
|
function formatStreamingDiff(diff: string, rawPath: string, uiTheme: Theme, label = "streaming"): string {
|
|
237
237
|
if (!diff) return "";
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
238
|
+
// "Cursor" tail window: pin the last EDIT_STREAMING_PREVIEW_LINES rows to the
|
|
239
|
+
// bottom of the diff so freshly streamed changes stay on screen, and accept
|
|
240
|
+
// the trailing rows "from the back" once the diff outgrows the window. The
|
|
241
|
+
// whole-file diff is recomputed on every streamed chunk and its Myers
|
|
242
|
+
// alignment is not monotonic in payload length, so a hunk-aware window that
|
|
243
|
+
// kept whole change segments gained and lost rows tick to tick — the box
|
|
244
|
+
// stuttered, and the earlier high-water fix traded that for a half-empty
|
|
245
|
+
// rectangle. A strict fixed-height window keeps the box steady and always
|
|
246
|
+
// full of real diff context instead of blank padding.
|
|
247
|
+
const allLines = diff.replace(/\n+$/u, "").split("\n");
|
|
248
|
+
const hiddenLines = Math.max(0, allLines.length - EDIT_STREAMING_PREVIEW_LINES);
|
|
249
|
+
const visible = hiddenLines > 0 ? allLines.slice(hiddenLines) : allLines;
|
|
247
250
|
let text = "\n\n";
|
|
248
|
-
if (
|
|
251
|
+
if (hiddenLines > 0) {
|
|
252
|
+
const hiddenHunks = getDiffStats(allLines.slice(0, hiddenLines).join("\n")).hunks;
|
|
249
253
|
const remainder: string[] = [];
|
|
250
254
|
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
251
|
-
|
|
255
|
+
remainder.push(`${hiddenLines} more lines`);
|
|
252
256
|
text += `${uiTheme.fg("dim", `… (${remainder.join(", ")} above)`)}\n`;
|
|
253
257
|
}
|
|
254
|
-
text += renderDiffColored(
|
|
258
|
+
text += renderDiffColored(visible.join("\n"), { filePath: rawPath });
|
|
255
259
|
text += uiTheme.fg("dim", `\n(${label})`);
|
|
256
260
|
return text;
|
|
257
261
|
}
|