@ddtcorex/dsh-maestro-memory 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/lib/client.js +220 -365
- package/lib/memory/store.d.ts.map +1 -1
- package/lib/memory/store.js +15 -0
- package/lib/memory/store.js.map +1 -1
- package/lib/prompt/snapshot.d.ts.map +1 -1
- package/lib/prompt/snapshot.js +6 -2
- package/lib/prompt/snapshot.js.map +1 -1
- package/lib/types/client/index.d.ts +0 -4
- package/lib/types/client/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/index.tsx +527 -1003
- package/src/host/memory/store.ts +16 -0
- package/src/host/prompt/snapshot.ts +6 -2
package/src/host/memory/store.ts
CHANGED
|
@@ -373,6 +373,22 @@ export class MaestroMemoryStore {
|
|
|
373
373
|
const t = normalizeTarget(target)
|
|
374
374
|
let content = String(entry ?? '').trim()
|
|
375
375
|
if (!content) return { ok: false, error: 'empty content' }
|
|
376
|
+
// Guard against idle-loop spam: reject trivial placeholder entries that carry
|
|
377
|
+
// no new information (e.g. "Idle", "Idle — no change"). The snapshot
|
|
378
|
+
// discipline now says to skip when idle, but keep a code-level guard as
|
|
379
|
+
// defense-in-depth so a misbehaving model cannot flood daily/project.
|
|
380
|
+
const trivialStripped = content.replace(/^\[.*?\]\s*/g, '').trim().toLowerCase()
|
|
381
|
+
if (
|
|
382
|
+
trivialStripped.startsWith('idle') ||
|
|
383
|
+
trivialStripped.startsWith('no change') ||
|
|
384
|
+
trivialStripped.startsWith('awaiting') ||
|
|
385
|
+
trivialStripped.startsWith('waiting')
|
|
386
|
+
) {
|
|
387
|
+
// Idle/placeholder entries carry no new information; treat as duplicate/no-op
|
|
388
|
+
// rather than error so the caller can proceed without retrying. Cap at 150
|
|
389
|
+
// chars so legitimate long entries that merely mention "idle" are not blocked.
|
|
390
|
+
if (trivialStripped.length < 150) return { ok: true, duplicate: true }
|
|
391
|
+
}
|
|
376
392
|
// Desensitize by default (opt-out via {desensitize:false} for tests/internal)
|
|
377
393
|
const doDesensitize = opts.desensitize !== false
|
|
378
394
|
if (doDesensitize) {
|
|
@@ -99,12 +99,15 @@ export function renderSnapshot(
|
|
|
99
99
|
|
|
100
100
|
// Recent daily slot (512B) — last 2 days' newest entries
|
|
101
101
|
// Keeps recent context without exceeding cap; full logs remain query-only.
|
|
102
|
+
// Use local calendar (matching store.todayStamp) to avoid UTC/local drift near midnight.
|
|
102
103
|
try {
|
|
103
104
|
const recentDaily: string[] = []
|
|
104
105
|
for (let i = 0; i < 2; i++) {
|
|
105
106
|
const d = new Date()
|
|
106
107
|
d.setDate(d.getDate() - i)
|
|
107
|
-
const
|
|
108
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0')
|
|
109
|
+
const dd = String(d.getDate()).padStart(2, '0')
|
|
110
|
+
const ds = `${d.getFullYear()}-${mm}-${dd}`
|
|
108
111
|
try {
|
|
109
112
|
const list: string[] = store.list('daily', undefined, { date: ds } as any)
|
|
110
113
|
if (list.length) recentDaily.push(list[list.length - 1])
|
|
@@ -117,8 +120,9 @@ export function renderSnapshot(
|
|
|
117
120
|
} catch {}
|
|
118
121
|
|
|
119
122
|
// End-of-turn discipline note — verbatim contract (hardened: exactly once, always last)
|
|
123
|
+
// Conditional to avoid idle loops: only when turn produced meaningful progress.
|
|
120
124
|
const discipline =
|
|
121
|
-
`---\nEnd of every turn
|
|
125
|
+
`---\nEnd of every turn — if this turn produced meaningful progress (code, decisions, learnings, or next steps): 1. Write daily+project via memory entries (daily+project in one call, skip if idle/waiting or no new information — never write entries containing only 'Idle' or placeholders) 2. Check dtodo list only if relevant (bounded, max 8)\nFor important project decisions (convention, incident, infra) use memory_suggest target=key with reason, not memory add.`
|
|
122
126
|
// Defensive: strip any pre-existing discipline entry (should never occur — parts is fresh per call)
|
|
123
127
|
// then append exactly once so the note is guaranteed last even for empty stores or repeated calls.
|
|
124
128
|
const deduped = parts.filter((p) => p !== discipline)
|