iterate-plugin 2.12.2 → 3.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/README.md +28 -12
- package/README.zh-CN.md +2 -1
- package/dist/approval-gate.js +16 -2
- package/dist/config-loader.js +5 -0
- package/dist/git-scope.js +61 -7
- package/dist/index.js +16 -6
- package/dist/session-hooks.js +36 -11
- package/dist/skill-prompt.js +3 -0
- package/dist/tools/decision-log.js +10 -1
- package/dist/tools/defense-events.js +260 -0
- package/dist/tools/defense-store.js +97 -0
- package/dist/tools/experience-bank.js +248 -0
- package/dist/tools/experience-store.js +132 -0
- package/dist/tools/quality-gate.js +180 -0
- package/dist/tools/quality-store.js +174 -0
- package/lib/client.js +662 -103
- package/lib/parse.js +93 -0
- package/package.json +7 -6
- package/src/approval-gate.ts +14 -2
- package/src/client/index.ts +542 -49
- package/src/config-loader.ts +5 -0
- package/src/git-scope.ts +48 -7
- package/src/index.ts +16 -6
- package/src/session-hooks.ts +33 -11
- package/src/skill-prompt.ts +3 -0
- package/src/tools/checkpoint.ts +1 -1
- package/src/tools/config.ts +1 -1
- package/src/tools/decision-log.ts +11 -2
- package/src/tools/defense-events.ts +295 -0
- package/src/tools/defense-store.ts +113 -0
- package/src/tools/experience-bank.ts +264 -0
- package/src/tools/experience-store.ts +160 -0
- package/src/tools/fix.ts +1 -1
- package/src/tools/history.ts +1 -1
- package/src/tools/prune.ts +1 -1
- package/src/tools/quality-gate.ts +193 -0
- package/src/tools/quality-store.ts +199 -0
- package/src/tools/review.ts +1 -1
- package/src/tools/transcript.ts +1 -1
- package/src/tools/triage.ts +1 -1
- package/src/types.ts +118 -0
package/lib/parse.js
CHANGED
|
@@ -1427,4 +1427,97 @@ export function buildRuntimeStatusGuide() {
|
|
|
1427
1427
|
'清理状态:让模型调用 iterate_prune(默认 dry-run,只报告不删除,显式 dryRun:false 才真正清理)。',
|
|
1428
1428
|
]
|
|
1429
1429
|
return lines.join('\n')
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// ─── Runtime-observatory UI pure helpers ─────────────────────────────────────
|
|
1433
|
+
|
|
1434
|
+
/**
|
|
1435
|
+
* Filter the live reviewer-activity feed by activity type. An empty/unknown
|
|
1436
|
+
* `type` matches everything; entries are returned in their original (newest
|
|
1437
|
+
* first) order. Purely defensive: non-array input yields [].
|
|
1438
|
+
*
|
|
1439
|
+
* @param {unknown} entries
|
|
1440
|
+
* @param {unknown} type
|
|
1441
|
+
* @returns {Array<Record<string, unknown>>}
|
|
1442
|
+
*/
|
|
1443
|
+
export function filterLiveEntries(entries, type) {
|
|
1444
|
+
const list = Array.isArray(entries) ? entries : []
|
|
1445
|
+
const t = typeof type === 'string' ? type.trim() : ''
|
|
1446
|
+
if (!t) return list.slice()
|
|
1447
|
+
return list.filter((e) => e && typeof e === 'object' && String(e.type ?? '') === t)
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* Filter decision-timeline entries by type / round / free-text search, then
|
|
1452
|
+
* sort newest first by timestamp string (timeline entries are not guaranteed
|
|
1453
|
+
* to be reverse-ordered in the manifest).
|
|
1454
|
+
*
|
|
1455
|
+
* - `type`: exact `entry.type` match when non-empty.
|
|
1456
|
+
* - `round`: exact `entry.round` string match when non-empty.
|
|
1457
|
+
* - `search`: case-insensitive substring over type + round + JSON data.
|
|
1458
|
+
*
|
|
1459
|
+
* @param {unknown} entries
|
|
1460
|
+
* @param {{ type?: unknown, round?: unknown, search?: unknown } | null | undefined} opts
|
|
1461
|
+
* @returns {Array<Record<string, unknown>>}
|
|
1462
|
+
*/
|
|
1463
|
+
export function filterTimelineEntries(entries, opts) {
|
|
1464
|
+
const list = Array.isArray(entries) ? entries : []
|
|
1465
|
+
const o = opts && typeof opts === 'object' ? opts : {}
|
|
1466
|
+
const type = typeof o.type === 'string' ? o.type : ''
|
|
1467
|
+
const round = typeof o.round === 'string' ? o.round : ''
|
|
1468
|
+
const q = typeof o.search === 'string' ? o.search.trim().toLowerCase() : ''
|
|
1469
|
+
const filtered = list.filter((t) => {
|
|
1470
|
+
if (!t || typeof t !== 'object') return false
|
|
1471
|
+
if (type && String(t.type ?? '') !== type) return false
|
|
1472
|
+
if (round && String(t.round ?? '') !== round) return false
|
|
1473
|
+
if (q) {
|
|
1474
|
+
const hay = [String(t.type ?? ''), String(t.round ?? ''), JSON.stringify(t.data ?? {})].join(' ').toLowerCase()
|
|
1475
|
+
if (hay.indexOf(q) < 0) return false
|
|
1476
|
+
}
|
|
1477
|
+
return true
|
|
1478
|
+
})
|
|
1479
|
+
return filtered
|
|
1480
|
+
.slice()
|
|
1481
|
+
.sort((a, b) => String(b.timestamp ?? '').localeCompare(String(a.timestamp ?? '')))
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* Serialize the full observatory state (manifest + live feed) into a JSON
|
|
1486
|
+
* string the client can copy/export. Always includes an `exportedAt` stamp and
|
|
1487
|
+
* guards against non-serializable / oversized payloads by falling back to the
|
|
1488
|
+
* manifest only.
|
|
1489
|
+
*
|
|
1490
|
+
* @param {unknown} manifest
|
|
1491
|
+
* @param {unknown} live
|
|
1492
|
+
* @returns {string}
|
|
1493
|
+
*/
|
|
1494
|
+
export function serializeObservatoryExport(manifest, live) {
|
|
1495
|
+
const payload = {
|
|
1496
|
+
exportedAt: new Date().toISOString(),
|
|
1497
|
+
manifest: manifest && typeof manifest === 'object' ? manifest : null,
|
|
1498
|
+
live: Array.isArray(live) ? live : [],
|
|
1499
|
+
}
|
|
1500
|
+
try {
|
|
1501
|
+
return JSON.stringify(payload, null, 2)
|
|
1502
|
+
} catch {
|
|
1503
|
+
// A cyclic / non-serializable manifest must not crash the copy action.
|
|
1504
|
+
return JSON.stringify({ exportedAt: payload.exportedAt, manifest: null, live: [] }, null, 2)
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/**
|
|
1509
|
+
* Latest non-empty workflow phase name (plan / review / fix / validate /
|
|
1510
|
+
* report …) from the transcript manifest's phase list. The last recorded
|
|
1511
|
+
* phase is the one the run is currently in (or last finished).
|
|
1512
|
+
*
|
|
1513
|
+
* @param {unknown} phases
|
|
1514
|
+
* @returns {string}
|
|
1515
|
+
*/
|
|
1516
|
+
export function latestPhase(phases) {
|
|
1517
|
+
const list = Array.isArray(phases) ? phases : []
|
|
1518
|
+
let latest = ''
|
|
1519
|
+
for (const p of list) {
|
|
1520
|
+
if (typeof p === 'string' && p.trim()) latest = p.trim()
|
|
1521
|
+
}
|
|
1522
|
+
return latest
|
|
1430
1523
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-plugin",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus
|
|
3
|
+
"version": "3.2.1",
|
|
4
|
+
"description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness with quality command center and experience bank (v3.2). Features: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus dry-run pure-review mode, quality gate compute/persist, writable experience bank, defense events stream (record + bilingual labels), and native command buttons.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -60,13 +60,14 @@
|
|
|
60
60
|
"test:validate": "tsx --test test/validate.test.ts"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@deepseek-ai/cordis": "4.0.
|
|
64
|
-
"@deepseek-ai/dsh-tools": "0.1.
|
|
63
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
64
|
+
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-util-values": "0.1.2-rc.1",
|
|
65
66
|
"js-yaml": "4.3.1"
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
68
|
-
"@deepseek-ai/dsh-jobs": "^0.1.
|
|
69
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
69
|
+
"@deepseek-ai/dsh-jobs": "^0.1.2-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
70
71
|
"@types/js-yaml": "4.0.9",
|
|
71
72
|
"@types/node": "22.15.0",
|
|
72
73
|
"@types/react": "19.2.2",
|
package/src/approval-gate.ts
CHANGED
|
@@ -65,11 +65,23 @@ export function decideApproval(
|
|
|
65
65
|
execution: ToolExecutionLike,
|
|
66
66
|
policy: 'ask' | 'deny' | 'allow',
|
|
67
67
|
): ApprovalDecision {
|
|
68
|
-
|
|
68
|
+
// Defensive reads: a hostile/proxied execution object must degrade to "not
|
|
69
|
+
// our tool" (allow) rather than throw inside the gate.
|
|
70
|
+
let name = ''
|
|
71
|
+
try {
|
|
72
|
+
name = typeof execution?.name === 'string' ? execution.name : ''
|
|
73
|
+
} catch {
|
|
74
|
+
name = ''
|
|
75
|
+
}
|
|
69
76
|
if (!name) return { kind: 'allow' }
|
|
70
77
|
if (!DESTRUCTIVE_TOOLS.has(name)) return { kind: 'allow' }
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
let rawArgs: unknown
|
|
80
|
+
try {
|
|
81
|
+
rawArgs = execution.arguments
|
|
82
|
+
} catch {
|
|
83
|
+
rawArgs = undefined
|
|
84
|
+
}
|
|
73
85
|
const args: Record<string, unknown> =
|
|
74
86
|
rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
|
|
75
87
|
? (rawArgs as Record<string, unknown>)
|