pi-anchor 0.2.3 → 0.3.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/README.md +29 -14
- package/README_zh.md +29 -14
- package/assets/hero-en.svg +87 -0
- package/assets/hero-zh.svg +87 -0
- package/assets/hero.svg +108 -0
- package/bin/anchor.js +38 -51
- package/dist/{chunk-G72UHPBN.js → chunk-Q7IILJFA.js} +165 -43
- package/dist/extension.cjs +291 -62
- package/dist/extension.d.cts +16 -1
- package/dist/extension.d.ts +16 -1
- package/dist/extension.js +133 -23
- package/dist/index.cjs +169 -43
- package/dist/index.d.cts +63 -242
- package/dist/index.d.ts +63 -242
- package/dist/index.js +9 -1
- package/dist/store-t2_TDhJn.d.cts +210 -0
- package/dist/store-t2_TDhJn.d.ts +210 -0
- package/package.json +21 -5
- package/src/extension.ts +167 -30
- package/src/matcher.ts +51 -0
- package/src/settlement.ts +72 -6
- package/src/store.ts +25 -9
- package/src/tui.ts +77 -29
- package/src/types.ts +7 -7
package/src/settlement.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AnchorStore } from './store.ts';
|
|
2
|
-
import { findMatchedAnchors } from './matcher.ts';
|
|
2
|
+
import { findMatchedAnchors, isAnchorTargetingWorkspace } from './matcher.ts';
|
|
3
|
+
import type { SessionTouchObserver } from './observer.ts';
|
|
3
4
|
import type { Anchor, SettlementProposal } from './types.ts';
|
|
4
5
|
import { execSync } from 'node:child_process';
|
|
5
6
|
|
|
@@ -27,31 +28,96 @@ export function runPhysicalVerification(anchor: Anchor, cwd: string): { success:
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Real-time autonomous settlement during active session:
|
|
32
|
+
* Triggered immediately when a non-error shell command runs `git commit` or a matching `verifyCommand`.
|
|
33
|
+
* Works for BOTH file-bound and file-less (`files: []`) anchors!
|
|
34
|
+
*/
|
|
35
|
+
export function tryRealtimeAutoSettle(
|
|
36
|
+
store: AnchorStore,
|
|
37
|
+
observer: SessionTouchObserver,
|
|
38
|
+
cwd?: string,
|
|
39
|
+
executedCommand?: string
|
|
40
|
+
): Array<{ anchor: Anchor; reason: string }> {
|
|
41
|
+
const candidates = store.list({ cwd }).filter(a => a.status === 'active' || a.status === 'sleeping');
|
|
42
|
+
if (candidates.length === 0) return [];
|
|
43
|
+
|
|
44
|
+
const settledList: Array<{ anchor: Anchor; reason: string }> = [];
|
|
45
|
+
const cmdTrimmed = (executedCommand || '').trim();
|
|
46
|
+
|
|
47
|
+
for (const a of candidates) {
|
|
48
|
+
// 1. Check real-time Git Commit semantic match (works even when a.files is empty!)
|
|
49
|
+
if (observer.hasCommitted()) {
|
|
50
|
+
const commitMatch = observer.matchesCommit(a);
|
|
51
|
+
if (commitMatch.matched && (!cwd || isAnchorTargetingWorkspace(a, cwd))) {
|
|
52
|
+
const settled = store.settle(a.id, {
|
|
53
|
+
settledBy: 'verification-test',
|
|
54
|
+
summary: `Auto-settled via Git commit: ${commitMatch.message}`,
|
|
55
|
+
touchedFiles: observer.getModifiedFiles()
|
|
56
|
+
});
|
|
57
|
+
settledList.push({ anchor: settled, reason: `git commit ("${commitMatch.message}")` });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Check if the executed shell command directly ran and passed the anchor's verifyCommand
|
|
63
|
+
if (a.verifyCommand && cmdTrimmed && cmdTrimmed.includes(a.verifyCommand.trim())) {
|
|
64
|
+
const settled = store.settle(a.id, {
|
|
65
|
+
settledBy: 'verification-test',
|
|
66
|
+
summary: `Auto-settled via passing command: ${a.verifyCommand}`,
|
|
67
|
+
touchedFiles: observer.getModifiedFiles()
|
|
68
|
+
});
|
|
69
|
+
settledList.push({ anchor: settled, reason: `verifyCommand (${a.verifyCommand})` });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return settledList;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generate settlement candidates based on files touched or commits made during the session
|
|
31
78
|
*/
|
|
32
79
|
export function generateSettlementProposals(
|
|
33
80
|
store: AnchorStore,
|
|
34
81
|
touchedFiles: string[],
|
|
35
|
-
cwd?: string
|
|
82
|
+
cwd?: string,
|
|
83
|
+
observer?: SessionTouchObserver
|
|
36
84
|
): SettlementProposal[] {
|
|
37
|
-
// Only match active/sleeping anchors belonging to current workspace
|
|
38
85
|
const activeAndSleeping = store.list({ cwd }).filter(a => a.status === 'active' || a.status === 'sleeping');
|
|
39
86
|
const matches = findMatchedAnchors(activeAndSleeping, touchedFiles);
|
|
87
|
+
const seenIds = new Set<string>();
|
|
40
88
|
|
|
41
89
|
const proposals: SettlementProposal[] = [];
|
|
42
90
|
|
|
43
91
|
for (const match of matches) {
|
|
92
|
+
seenIds.add(match.anchor.id);
|
|
44
93
|
const fileList = match.matchedFiles.slice(0, 3).join(', ');
|
|
45
|
-
const moreSuffix = match.matchedFiles.length > 3 ? `
|
|
94
|
+
const moreSuffix = match.matchedFiles.length > 3 ? ` (+${match.matchedFiles.length - 3} more)` : '';
|
|
46
95
|
|
|
47
96
|
proposals.push({
|
|
48
97
|
anchor: match.anchor,
|
|
49
98
|
matchedFiles: match.matchedFiles,
|
|
50
|
-
reason:
|
|
99
|
+
reason: `Modified ${fileList}${moreSuffix}`,
|
|
51
100
|
recommendedAction: match.score >= 0.85 ? 'settle' : 'defer'
|
|
52
101
|
});
|
|
53
102
|
}
|
|
54
103
|
|
|
104
|
+
// Also include file-less (or un-matched) anchors if a git commit message matched them
|
|
105
|
+
if (observer && observer.hasCommitted()) {
|
|
106
|
+
for (const a of activeAndSleeping) {
|
|
107
|
+
if (seenIds.has(a.id)) continue;
|
|
108
|
+
const cm = observer.matchesCommit(a);
|
|
109
|
+
if (cm.matched) {
|
|
110
|
+
seenIds.add(a.id);
|
|
111
|
+
proposals.push({
|
|
112
|
+
anchor: a,
|
|
113
|
+
matchedFiles: touchedFiles.slice(0, 3),
|
|
114
|
+
reason: `Matched git commit: "${cm.message}"`,
|
|
115
|
+
recommendedAction: 'settle'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
55
121
|
return proposals;
|
|
56
122
|
}
|
|
57
123
|
|
package/src/store.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
AnchorStoreState
|
|
12
12
|
} from './types.ts';
|
|
13
13
|
import { DEFAULT_DECAY_POLICY, DURABLE_DECAY_POLICY, EPHEMERAL_DECAY_POLICY } from './types.ts';
|
|
14
|
-
import { normalizePath, isSameOrSubPath } from './matcher.ts';
|
|
14
|
+
import { normalizePath, isSameOrSubPath, extractPathsFromText, isAnchorTargetingWorkspace } from './matcher.ts';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Detect whether a task is a short-term ephemeral reminder or a long-term architectural vision
|
|
@@ -31,15 +31,16 @@ export function detectDurability(title: string): AnchorDurability {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Get custom ephemeral decay policy based on temporal span
|
|
34
|
+
* Get custom ephemeral decay policy based on temporal span.
|
|
35
|
+
* Note: graveyardDays is always 9999 so unfinished tasks are NEVER silently deleted.
|
|
35
36
|
*/
|
|
36
37
|
export function getEphemeralDecayPolicy(title: string): AnchorDecayPolicy {
|
|
37
38
|
const lower = title.toLowerCase();
|
|
38
39
|
if (lower.includes('大后天')) {
|
|
39
|
-
return { activeDays:
|
|
40
|
+
return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
|
|
40
41
|
}
|
|
41
42
|
if (lower.includes('后天')) {
|
|
42
|
-
return { activeDays:
|
|
43
|
+
return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
|
|
43
44
|
}
|
|
44
45
|
return { ...EPHEMERAL_DECAY_POLICY };
|
|
45
46
|
}
|
|
@@ -415,7 +416,22 @@ export class AnchorStore {
|
|
|
415
416
|
const now = Date.now();
|
|
416
417
|
const id = this.generateId(state);
|
|
417
418
|
|
|
418
|
-
const
|
|
419
|
+
const explicitFiles = (input.files || []).map(f => normalizePath(f)).filter(Boolean);
|
|
420
|
+
const inferredFiles = explicitFiles.length > 0
|
|
421
|
+
? explicitFiles
|
|
422
|
+
: extractPathsFromText(`${input.title} ${input.description || ''}`);
|
|
423
|
+
|
|
424
|
+
let cwd = input.cwd ? normalizePath(input.cwd) : '';
|
|
425
|
+
// If project is an explicit absolute path (e.g. "D:/College/优化"), align cwd to it so workspace matching is never split
|
|
426
|
+
if (input.project && (/^[a-zA-Z]:[\\/]/.test(input.project.trim()) || input.project.trim().startsWith('/'))) {
|
|
427
|
+
cwd = normalizePath(input.project.trim());
|
|
428
|
+
} else if (inferredFiles.length > 0 && (/^[a-zA-Z]:\//.test(inferredFiles[0]) || inferredFiles[0].startsWith('/'))) {
|
|
429
|
+
const firstDir = path.dirname(inferredFiles[0]);
|
|
430
|
+
if (firstDir && firstDir !== '.') {
|
|
431
|
+
cwd = normalizePath(firstDir);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
419
435
|
const projectName = input.project
|
|
420
436
|
? input.project.trim()
|
|
421
437
|
: (cwd ? path.basename(cwd) : 'global');
|
|
@@ -442,7 +458,7 @@ export class AnchorStore {
|
|
|
442
458
|
createdAt: now,
|
|
443
459
|
updatedAt: now,
|
|
444
460
|
lastTouchedAt: now,
|
|
445
|
-
files:
|
|
461
|
+
files: inferredFiles,
|
|
446
462
|
tags: (input.tags || []).map(t => t.trim()).filter(Boolean),
|
|
447
463
|
verifyCommand: input.verifyCommand?.trim() || undefined,
|
|
448
464
|
decay: input.decay ? { ...input.decay } : { ...decayPolicy }
|
|
@@ -497,10 +513,10 @@ export class AnchorStore {
|
|
|
497
513
|
if (filter?.priority && a.priority !== filter.priority) return false;
|
|
498
514
|
|
|
499
515
|
// Project context filtering:
|
|
500
|
-
// If an anchor has specific code files attached,
|
|
501
|
-
// If an anchor has NO code files attached (
|
|
516
|
+
// If an anchor has specific code files attached, match against cwd, project path/name, or file paths.
|
|
517
|
+
// If an anchor has NO code files attached (general tasks, memos), it is universal and visible everywhere!
|
|
502
518
|
if (!filter?.all && targetCwd) {
|
|
503
|
-
if (a.files.length > 0 && a.cwd && !
|
|
519
|
+
if (a.files.length > 0 && (a.cwd || a.project) && !isAnchorTargetingWorkspace(a, targetCwd)) {
|
|
504
520
|
return false;
|
|
505
521
|
}
|
|
506
522
|
}
|
package/src/tui.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
2
|
import { AnchorStore, getTodayDateString } from './store.ts';
|
|
3
|
+
import { isAnchorTargetingWorkspace } from './matcher.ts';
|
|
3
4
|
import type { Anchor } from './types.ts';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
|
|
@@ -57,6 +58,40 @@ export function groupAnchorsByQuadrant(anchors: Anchor[], now: number = Date.now
|
|
|
57
58
|
return groups;
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
const PRIORITY_RANK: Record<string, number> = { p0: 0, p1: 1, p2: 2 };
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Sort unfinished anchors into a single compact, priority-driven engineering list:
|
|
65
|
+
* 1. Active before Sleeping (quiet)
|
|
66
|
+
* 2. Matching current workspace before other workspaces
|
|
67
|
+
* 3. Due Today / Overdue before open-ended
|
|
68
|
+
* 4. Priority (P0 -> P1 -> P2)
|
|
69
|
+
*/
|
|
70
|
+
export function sortAnchorsForDisplay(anchors: Anchor[], cwd?: string, now: number = Date.now()): Anchor[] {
|
|
71
|
+
const todayStr = getTodayDateString(now);
|
|
72
|
+
return [...anchors].sort((a, b) => {
|
|
73
|
+
// 1. Active before sleeping
|
|
74
|
+
if (a.status !== b.status) {
|
|
75
|
+
return a.status === 'active' ? -1 : 1;
|
|
76
|
+
}
|
|
77
|
+
// 2. Current workspace match first
|
|
78
|
+
if (cwd) {
|
|
79
|
+
const aMatch = isAnchorTargetingWorkspace(a, cwd) ? 0 : 1;
|
|
80
|
+
const bMatch = isAnchorTargetingWorkspace(b, cwd) ? 0 : 1;
|
|
81
|
+
if (aMatch !== bMatch) return aMatch - bMatch;
|
|
82
|
+
}
|
|
83
|
+
// 3. Due today / overdue first
|
|
84
|
+
const aDue = a.targetDate && a.targetDate <= todayStr ? 0 : 1;
|
|
85
|
+
const bDue = b.targetDate && b.targetDate <= todayStr ? 0 : 1;
|
|
86
|
+
if (aDue !== bDue) return aDue - bDue;
|
|
87
|
+
// 4. Priority P0 -> P1 -> P2
|
|
88
|
+
const pA = PRIORITY_RANK[a.priority] ?? 1;
|
|
89
|
+
const pB = PRIORITY_RANK[b.priority] ?? 1;
|
|
90
|
+
if (pA !== pB) return pA - pB;
|
|
91
|
+
return a.createdAt - b.createdAt;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
60
95
|
const ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
61
96
|
|
|
62
97
|
/**
|
|
@@ -286,15 +321,23 @@ export function formatRemainingTtl(anchor: Anchor, now: number = Date.now()): st
|
|
|
286
321
|
}
|
|
287
322
|
|
|
288
323
|
/**
|
|
289
|
-
* Format origin
|
|
324
|
+
* Format origin workspace / project label concisely
|
|
290
325
|
*/
|
|
291
326
|
export function formatOrigin(anchor: Anchor | string): string {
|
|
292
327
|
if (typeof anchor === 'object') {
|
|
293
|
-
if (
|
|
294
|
-
|
|
328
|
+
if (anchor.project && anchor.project !== 'global') {
|
|
329
|
+
const normProj = anchor.project.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
330
|
+
if (normProj.includes('/')) {
|
|
331
|
+
const parts = normProj.split('/').filter(Boolean);
|
|
332
|
+
return parts.slice(-2).join('/');
|
|
333
|
+
}
|
|
334
|
+
return normProj;
|
|
335
|
+
}
|
|
336
|
+
if (!anchor.cwd) return 'global';
|
|
337
|
+
return path.basename(anchor.cwd.replace(/\\/g, '/')) || 'global';
|
|
295
338
|
}
|
|
296
339
|
if (!anchor) return 'global';
|
|
297
|
-
return path.basename(anchor) || 'global';
|
|
340
|
+
return path.basename(anchor.replace(/\\/g, '/')) || 'global';
|
|
298
341
|
}
|
|
299
342
|
|
|
300
343
|
/**
|
|
@@ -372,7 +415,7 @@ export function updateStartupBanner(ctx: ExtensionContext, store: AnchorStore):
|
|
|
372
415
|
}
|
|
373
416
|
|
|
374
417
|
/**
|
|
375
|
-
* Clean,
|
|
418
|
+
* Clean, flat priority-sorted engineering checklist (includes both active and quiet/sleeping tasks so nothing is ever lost)
|
|
376
419
|
*/
|
|
377
420
|
export async function openAnchorDashboard(
|
|
378
421
|
ctx: ExtensionContext,
|
|
@@ -380,66 +423,67 @@ export async function openAnchorDashboard(
|
|
|
380
423
|
): Promise<void> {
|
|
381
424
|
if (!hasActiveUI(ctx)) return;
|
|
382
425
|
|
|
383
|
-
const
|
|
384
|
-
|
|
426
|
+
const allUnfinished = store
|
|
427
|
+
.list({ cwd: ctx.cwd, all: true })
|
|
428
|
+
.filter(a => a.status === 'active' || a.status === 'sleeping');
|
|
429
|
+
|
|
430
|
+
if (allUnfinished.length === 0) {
|
|
385
431
|
ctx.ui.notify('⌖ No active anchors. Use /pin <task> to record.', 'info');
|
|
386
432
|
return;
|
|
387
433
|
}
|
|
388
434
|
|
|
389
|
-
const
|
|
390
|
-
const sortedList = [
|
|
391
|
-
...groups.today,
|
|
392
|
-
...groups.upcoming,
|
|
393
|
-
...groups.habits,
|
|
394
|
-
...groups.backlog
|
|
395
|
-
];
|
|
435
|
+
const sortedList = sortAnchorsForDisplay(allUnfinished, ctx.cwd);
|
|
396
436
|
|
|
397
437
|
const optionMap = new Map<string, Anchor>();
|
|
398
438
|
const displayOptions: string[] = [];
|
|
399
439
|
const termCols = process.stdout?.columns || 96;
|
|
400
440
|
const isNarrow = termCols < 88;
|
|
401
|
-
const titleWidth = isNarrow ? Math.max(18, termCols - 36) :
|
|
441
|
+
const titleWidth = isNarrow ? Math.max(18, termCols - 36) : 36;
|
|
402
442
|
|
|
403
443
|
for (let i = 0; i < sortedList.length; i++) {
|
|
404
444
|
const a = sortedList[i];
|
|
405
445
|
const num = a.id.replace(/^anc-/, '').padStart(2, '0');
|
|
406
|
-
const
|
|
446
|
+
const badge = a.status === 'sleeping'
|
|
447
|
+
? padToWidth('[Quiet]', 10)
|
|
448
|
+
: padToWidth(`[${a.priority.toUpperCase()}]`, 10);
|
|
407
449
|
const origin = formatOrigin(a);
|
|
408
|
-
const target = formatTargetDate(a);
|
|
450
|
+
const target = a.status === 'sleeping' ? 'Quiet (0 tok)' : formatTargetDate(a);
|
|
409
451
|
const created = formatCreationTime(a.createdAt);
|
|
410
452
|
|
|
411
453
|
const titleWithFiles = (a.files && a.files.length > 0)
|
|
412
454
|
? `${a.title} [${a.files.slice(0, 1).join(', ')}]`
|
|
413
455
|
: a.title;
|
|
414
456
|
|
|
415
|
-
// Clean tabular column alignment: ID, Category, Title, Project, Target, Created
|
|
416
457
|
const colNum = `${num} `;
|
|
417
458
|
const colTitle = padToWidth(titleWithFiles, titleWidth);
|
|
418
|
-
const colOrigin = padToWidth(origin,
|
|
419
|
-
const colTarget = padToWidth(target,
|
|
459
|
+
const colOrigin = padToWidth(origin, 12);
|
|
460
|
+
const colTarget = padToWidth(target, 14);
|
|
420
461
|
const colCreated = created;
|
|
421
462
|
|
|
422
463
|
const label = isNarrow
|
|
423
|
-
? `${colNum}${
|
|
424
|
-
: `${colNum}${
|
|
464
|
+
? `${colNum}${badge}${colTitle} ${colTarget}`.trimEnd()
|
|
465
|
+
: `${colNum}${badge}${colTitle} ${colOrigin} ${colTarget} ${colCreated}`.trimEnd();
|
|
425
466
|
|
|
426
467
|
optionMap.set(label, a);
|
|
427
468
|
displayOptions.push(label);
|
|
428
469
|
}
|
|
429
470
|
|
|
430
|
-
const selected = await ctx.ui.select('⌖ Anchors (select to
|
|
471
|
+
const selected = await ctx.ui.select('⌖ Anchors (select to settle, wake, or inspect):', displayOptions);
|
|
431
472
|
if (!selected) return;
|
|
432
473
|
|
|
433
474
|
const anchor = optionMap.get(selected);
|
|
434
475
|
if (!anchor) return;
|
|
435
476
|
|
|
477
|
+
const actions = [
|
|
478
|
+
'✓ Settle (Mark completed and archive)',
|
|
479
|
+
...(anchor.status === 'sleeping' ? ['⚡ Wake up (Activate for current session)'] : []),
|
|
480
|
+
'ℹ View details & associated files',
|
|
481
|
+
'↩ Cancel'
|
|
482
|
+
];
|
|
483
|
+
|
|
436
484
|
const action = await ctx.ui.select(
|
|
437
485
|
`⌖ Anchor #${anchor.id}: "${anchor.title}"`,
|
|
438
|
-
|
|
439
|
-
'✓ Settle (Mark completed and archive)',
|
|
440
|
-
'ℹ View details & associated files',
|
|
441
|
-
'↩ Cancel'
|
|
442
|
-
]
|
|
486
|
+
actions
|
|
443
487
|
);
|
|
444
488
|
if (!action || action.startsWith('↩')) return;
|
|
445
489
|
|
|
@@ -450,9 +494,13 @@ export async function openAnchorDashboard(
|
|
|
450
494
|
: `⌖ Settled #${anchor.id}: "${anchor.title}" (/anchor undo to revert)`;
|
|
451
495
|
ctx.ui.notify(successMsg, 'info');
|
|
452
496
|
updateAnchorStatusBar(ctx, store);
|
|
497
|
+
} else if (action.startsWith('⚡')) {
|
|
498
|
+
store.touch(anchor.id);
|
|
499
|
+
ctx.ui.notify(`⌖ Woke up #${anchor.id}: "${anchor.title}"`, 'info');
|
|
500
|
+
updateAnchorStatusBar(ctx, store);
|
|
453
501
|
} else if (action.startsWith('ℹ')) {
|
|
454
502
|
const details = [
|
|
455
|
-
`#${anchor.id} [${anchor.priority.toUpperCase()}] ${anchor.title}`,
|
|
503
|
+
`#${anchor.id} [${anchor.priority.toUpperCase()} · ${anchor.status.toUpperCase()}] ${anchor.title}`,
|
|
456
504
|
anchor.description ? `Description: ${anchor.description}` : null,
|
|
457
505
|
anchor.targetDate ? `Target Date: ${anchor.targetDate} (${formatTargetDate(anchor)})` : null,
|
|
458
506
|
anchor.files && anchor.files.length > 0 ? `Files: ${anchor.files.join(', ')}` : null,
|
package/src/types.ts
CHANGED
|
@@ -87,19 +87,19 @@ export interface SettlementProposal {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export const EPHEMERAL_DECAY_POLICY: AnchorDecayPolicy = {
|
|
90
|
-
activeDays:
|
|
91
|
-
sleepDays:
|
|
92
|
-
graveyardDays:
|
|
90
|
+
activeDays: 7, // 7 days active before quiet sleep
|
|
91
|
+
sleepDays: 30,
|
|
92
|
+
graveyardDays: 9999 // Never silently auto-delete unfinished tasks
|
|
93
93
|
};
|
|
94
94
|
|
|
95
95
|
export const DURABLE_DECAY_POLICY: AnchorDecayPolicy = {
|
|
96
|
-
activeDays:
|
|
96
|
+
activeDays: 7, // 7 days active before quiet sleep
|
|
97
97
|
sleepDays: 30,
|
|
98
98
|
graveyardDays: 9999 // Never permanently auto-dropped; preserved indefinitely
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
export const DEFAULT_DECAY_POLICY: AnchorDecayPolicy = {
|
|
102
|
-
activeDays:
|
|
103
|
-
sleepDays:
|
|
104
|
-
graveyardDays:
|
|
102
|
+
activeDays: 7,
|
|
103
|
+
sleepDays: 30,
|
|
104
|
+
graveyardDays: 9999
|
|
105
105
|
};
|