@compilr-dev/agents 0.2.1 → 0.3.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/dist/agent.d.ts +11 -0
- package/dist/agent.js +210 -122
- package/dist/anchors/manager.d.ts +34 -0
- package/dist/anchors/manager.js +86 -0
- package/dist/anchors/types.d.ts +29 -0
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/ollama.js +3 -3
- package/dist/skills/index.js +476 -235
- package/dist/tools/builtin/ask-user-simple.d.ts +64 -0
- package/dist/tools/builtin/ask-user-simple.js +149 -0
- package/dist/tools/builtin/ask-user.d.ts +85 -0
- package/dist/tools/builtin/ask-user.js +195 -0
- package/dist/tools/builtin/backlog.d.ts +121 -0
- package/dist/tools/builtin/backlog.js +368 -0
- package/dist/tools/builtin/bash.js +180 -13
- package/dist/tools/builtin/index.d.ts +11 -1
- package/dist/tools/builtin/index.js +16 -0
- package/dist/tools/builtin/task.d.ts +14 -4
- package/dist/tools/builtin/task.js +9 -9
- package/dist/tools/define.d.ts +7 -0
- package/dist/tools/define.js +1 -0
- package/dist/tools/registry.d.ts +3 -2
- package/dist/tools/registry.js +19 -6
- package/dist/tools/types.d.ts +29 -2
- package/package.json +1 -1
package/dist/agent.d.ts
CHANGED
|
@@ -35,10 +35,18 @@ export type AgentEvent = {
|
|
|
35
35
|
type: 'tool_start';
|
|
36
36
|
name: string;
|
|
37
37
|
input: Record<string, unknown>;
|
|
38
|
+
toolUseId: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'tool_output';
|
|
41
|
+
toolUseId: string;
|
|
42
|
+
toolName: string;
|
|
43
|
+
output: string;
|
|
44
|
+
stream?: 'stdout' | 'stderr';
|
|
38
45
|
} | {
|
|
39
46
|
type: 'tool_end';
|
|
40
47
|
name: string;
|
|
41
48
|
result: ToolExecutionResult;
|
|
49
|
+
toolUseId: string;
|
|
42
50
|
} | {
|
|
43
51
|
type: 'iteration_end';
|
|
44
52
|
iteration: number;
|
|
@@ -1449,6 +1457,9 @@ export declare class Agent {
|
|
|
1449
1457
|
/**
|
|
1450
1458
|
* Generate a summary of messages using the LLM provider.
|
|
1451
1459
|
* Used for context summarization when approaching limits.
|
|
1460
|
+
*
|
|
1461
|
+
* If anchors are configured, the summary will prioritize keeping
|
|
1462
|
+
* information related to anchor content (anchor-weighted summarization).
|
|
1452
1463
|
*/
|
|
1453
1464
|
private generateSummary;
|
|
1454
1465
|
/**
|
package/dist/agent.js
CHANGED
|
@@ -1621,13 +1621,34 @@ export class Agent {
|
|
|
1621
1621
|
messages.push(assistantMsg);
|
|
1622
1622
|
newMessages.push(assistantMsg);
|
|
1623
1623
|
// Execute tools and add results
|
|
1624
|
-
|
|
1625
|
-
|
|
1624
|
+
// Check if we can parallelize - only parallelize tools marked as parallel-safe
|
|
1625
|
+
const parallelTools = toolUses.filter((tu) => {
|
|
1626
|
+
const tool = this.toolRegistry.get(tu.name);
|
|
1627
|
+
return tool?.parallel === true;
|
|
1628
|
+
});
|
|
1629
|
+
const canParallelize = parallelTools.length > 1 && parallelTools.length === toolUses.length;
|
|
1630
|
+
// Helper to execute a single tool with all checks
|
|
1631
|
+
const executeSingleTool = async (toolUse) => {
|
|
1632
|
+
// Check for abort
|
|
1626
1633
|
if (signal?.aborted) {
|
|
1627
|
-
|
|
1628
|
-
|
|
1634
|
+
return {
|
|
1635
|
+
result: { success: false, error: 'Aborted' },
|
|
1636
|
+
toolResultMsg: {
|
|
1637
|
+
role: 'user',
|
|
1638
|
+
content: [
|
|
1639
|
+
{ type: 'tool_result', toolUseId: toolUse.id, content: 'Aborted', isError: true },
|
|
1640
|
+
],
|
|
1641
|
+
},
|
|
1642
|
+
skipped: true,
|
|
1643
|
+
aborted: true,
|
|
1644
|
+
};
|
|
1629
1645
|
}
|
|
1630
|
-
emit({
|
|
1646
|
+
emit({
|
|
1647
|
+
type: 'tool_start',
|
|
1648
|
+
name: toolUse.name,
|
|
1649
|
+
input: toolUse.input,
|
|
1650
|
+
toolUseId: toolUse.id,
|
|
1651
|
+
});
|
|
1631
1652
|
let result;
|
|
1632
1653
|
// Check permissions before execution
|
|
1633
1654
|
if (this.permissionManager) {
|
|
@@ -1646,27 +1667,23 @@ export class Agent {
|
|
|
1646
1667
|
success: false,
|
|
1647
1668
|
error: `Permission denied: ${permResult.reason ?? 'Tool execution not allowed'}`,
|
|
1648
1669
|
};
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
],
|
|
1670
|
+
emit({ type: 'tool_end', name: toolUse.name, result, toolUseId: toolUse.id });
|
|
1671
|
+
return {
|
|
1672
|
+
result,
|
|
1673
|
+
toolResultMsg: {
|
|
1674
|
+
role: 'user',
|
|
1675
|
+
content: [
|
|
1676
|
+
{
|
|
1677
|
+
type: 'tool_result',
|
|
1678
|
+
toolUseId: toolUse.id,
|
|
1679
|
+
content: `Error: ${result.error ?? 'Permission denied'}`,
|
|
1680
|
+
isError: true,
|
|
1681
|
+
},
|
|
1682
|
+
],
|
|
1683
|
+
},
|
|
1684
|
+
skipped: true,
|
|
1685
|
+
aborted: false,
|
|
1666
1686
|
};
|
|
1667
|
-
messages.push(toolResultMsg);
|
|
1668
|
-
newMessages.push(toolResultMsg);
|
|
1669
|
-
continue;
|
|
1670
1687
|
}
|
|
1671
1688
|
emit({ type: 'permission_granted', toolName: toolUse.name, level: permResult.level });
|
|
1672
1689
|
}
|
|
@@ -1676,37 +1693,31 @@ export class Agent {
|
|
|
1676
1693
|
if (guardrailResult.triggered) {
|
|
1677
1694
|
emit({ type: 'guardrail_triggered', result: guardrailResult });
|
|
1678
1695
|
if (!proceed) {
|
|
1679
|
-
// Guardrail blocked the execution
|
|
1680
1696
|
const message = guardrailResult.guardrail?.message ?? 'Operation blocked by guardrail';
|
|
1681
1697
|
emit({ type: 'guardrail_blocked', result: guardrailResult, message });
|
|
1682
1698
|
result = {
|
|
1683
1699
|
success: false,
|
|
1684
1700
|
error: `Guardrail blocked: ${message}`,
|
|
1685
1701
|
};
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
],
|
|
1702
|
+
emit({ type: 'tool_end', name: toolUse.name, result, toolUseId: toolUse.id });
|
|
1703
|
+
return {
|
|
1704
|
+
result,
|
|
1705
|
+
toolResultMsg: {
|
|
1706
|
+
role: 'user',
|
|
1707
|
+
content: [
|
|
1708
|
+
{
|
|
1709
|
+
type: 'tool_result',
|
|
1710
|
+
toolUseId: toolUse.id,
|
|
1711
|
+
content: `Error: ${result.error ?? 'Blocked by guardrail'}`,
|
|
1712
|
+
isError: true,
|
|
1713
|
+
},
|
|
1714
|
+
],
|
|
1715
|
+
},
|
|
1716
|
+
skipped: true,
|
|
1717
|
+
aborted: false,
|
|
1703
1718
|
};
|
|
1704
|
-
messages.push(toolResultMsg);
|
|
1705
|
-
newMessages.push(toolResultMsg);
|
|
1706
|
-
continue;
|
|
1707
1719
|
}
|
|
1708
1720
|
else if (guardrailResult.action === 'warn') {
|
|
1709
|
-
// Log warning and continue
|
|
1710
1721
|
const message = guardrailResult.guardrail?.message ?? 'Warning from guardrail';
|
|
1711
1722
|
emit({ type: 'guardrail_warning', result: guardrailResult, message });
|
|
1712
1723
|
}
|
|
@@ -1722,20 +1733,45 @@ export class Agent {
|
|
|
1722
1733
|
});
|
|
1723
1734
|
if (!beforeToolResult.proceed) {
|
|
1724
1735
|
result = beforeToolResult.skipResult ?? { success: false, error: 'Skipped by hook' };
|
|
1725
|
-
emit({ type: 'tool_end', name: toolUse.name, result });
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1736
|
+
emit({ type: 'tool_end', name: toolUse.name, result, toolUseId: toolUse.id });
|
|
1737
|
+
return {
|
|
1738
|
+
result,
|
|
1739
|
+
toolResultMsg: {
|
|
1740
|
+
role: 'user',
|
|
1741
|
+
content: [
|
|
1742
|
+
{
|
|
1743
|
+
type: 'tool_result',
|
|
1744
|
+
toolUseId: toolUse.id,
|
|
1745
|
+
content: result.success
|
|
1746
|
+
? JSON.stringify(result.result)
|
|
1747
|
+
: `Error: ${result.error ?? 'Unknown error'}`,
|
|
1748
|
+
isError: !result.success,
|
|
1749
|
+
},
|
|
1750
|
+
],
|
|
1751
|
+
},
|
|
1752
|
+
skipped: true,
|
|
1753
|
+
aborted: false,
|
|
1754
|
+
};
|
|
1730
1755
|
}
|
|
1731
1756
|
toolInput = beforeToolResult.input;
|
|
1732
1757
|
}
|
|
1733
1758
|
const toolStartTime = Date.now();
|
|
1734
1759
|
try {
|
|
1735
|
-
|
|
1760
|
+
const toolContext = {
|
|
1761
|
+
toolUseId: toolUse.id,
|
|
1762
|
+
onOutput: (output, stream) => {
|
|
1763
|
+
emit({
|
|
1764
|
+
type: 'tool_output',
|
|
1765
|
+
toolUseId: toolUse.id,
|
|
1766
|
+
toolName: toolUse.name,
|
|
1767
|
+
output,
|
|
1768
|
+
stream,
|
|
1769
|
+
});
|
|
1770
|
+
},
|
|
1771
|
+
};
|
|
1772
|
+
result = await this.toolRegistry.execute(toolUse.name, toolInput, toolContext);
|
|
1736
1773
|
}
|
|
1737
1774
|
catch (error) {
|
|
1738
|
-
// Run onError hooks for tool errors (thrown exceptions)
|
|
1739
1775
|
if (this.hooksManager && error instanceof Error) {
|
|
1740
1776
|
const errorResult = await this.hooksManager.runOnError({
|
|
1741
1777
|
...hookContext,
|
|
@@ -1760,8 +1796,7 @@ export class Agent {
|
|
|
1760
1796
|
};
|
|
1761
1797
|
}
|
|
1762
1798
|
}
|
|
1763
|
-
// Run onError hooks for failed tool results
|
|
1764
|
-
// The tool registry catches errors internally and returns { success: false }
|
|
1799
|
+
// Run onError hooks for failed tool results
|
|
1765
1800
|
if (this.hooksManager && !result.success) {
|
|
1766
1801
|
const syntheticError = new Error(result.error);
|
|
1767
1802
|
const errorResult = await this.hooksManager.runOnError({
|
|
@@ -1784,89 +1819,116 @@ export class Agent {
|
|
|
1784
1819
|
durationMs: Date.now() - toolStartTime,
|
|
1785
1820
|
});
|
|
1786
1821
|
}
|
|
1787
|
-
emit({ type: 'tool_end', name: toolUse.name, result });
|
|
1788
|
-
// Tool loop detection: check for consecutive identical calls
|
|
1789
|
-
if (this.maxConsecutiveToolCalls > 0) {
|
|
1790
|
-
const currentHash = hashToolCall(toolUse.name, toolUse.input);
|
|
1791
|
-
if (currentHash === lastToolCallHash) {
|
|
1792
|
-
consecutiveIdenticalCalls++;
|
|
1793
|
-
if (consecutiveIdenticalCalls >= this.maxConsecutiveToolCalls) {
|
|
1794
|
-
throw new ToolLoopError(toolUse.name, consecutiveIdenticalCalls, toolUse.input);
|
|
1795
|
-
}
|
|
1796
|
-
// Emit warning before throwing
|
|
1797
|
-
emit({
|
|
1798
|
-
type: 'tool_loop_warning',
|
|
1799
|
-
toolName: toolUse.name,
|
|
1800
|
-
consecutiveCalls: consecutiveIdenticalCalls,
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
else {
|
|
1804
|
-
// Different tool call, reset counter
|
|
1805
|
-
lastToolCallHash = currentHash;
|
|
1806
|
-
consecutiveIdenticalCalls = 1;
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
const toolCallEntry = { name: toolUse.name, input: toolUse.input, result };
|
|
1810
|
-
toolCalls.push(toolCallEntry);
|
|
1811
|
-
iterationToolCalls.push(toolCallEntry);
|
|
1822
|
+
emit({ type: 'tool_end', name: toolUse.name, result, toolUseId: toolUse.id });
|
|
1812
1823
|
// Build tool result content
|
|
1813
1824
|
let toolResultContent = result.success
|
|
1814
1825
|
? JSON.stringify(result.result)
|
|
1815
1826
|
: `Error: ${result.error ?? 'Unknown error'}`;
|
|
1816
|
-
// Context management
|
|
1817
|
-
if (this.contextManager && this.autoContextManagement) {
|
|
1827
|
+
// Context management (only for sequential - parallel handles this after)
|
|
1828
|
+
if (!canParallelize && this.contextManager && this.autoContextManagement) {
|
|
1818
1829
|
const estimatedTokens = this.contextManager.estimateTokens(toolResultContent);
|
|
1819
1830
|
const preflight = this.contextManager.canAddContent(estimatedTokens, 'toolResults');
|
|
1820
1831
|
if (!preflight.allowed) {
|
|
1821
1832
|
if (preflight.action === 'reject') {
|
|
1822
|
-
// Content too large - filter it
|
|
1823
1833
|
const filtered = this.contextManager.filterContent(toolResultContent, 'tool_result');
|
|
1824
1834
|
toolResultContent = filtered.content;
|
|
1825
1835
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1836
|
+
// Note: compact/summarize actions are complex with parallel execution
|
|
1837
|
+
// For now, only filter is applied during parallel; full context mgmt happens after
|
|
1838
|
+
}
|
|
1839
|
+
const finalTokens = this.contextManager.estimateTokens(toolResultContent);
|
|
1840
|
+
this.contextManager.addToCategory('toolResults', finalTokens);
|
|
1841
|
+
}
|
|
1842
|
+
return {
|
|
1843
|
+
result,
|
|
1844
|
+
toolResultMsg: {
|
|
1845
|
+
role: 'user',
|
|
1846
|
+
content: [
|
|
1847
|
+
{
|
|
1848
|
+
type: 'tool_result',
|
|
1849
|
+
toolUseId: toolUse.id,
|
|
1850
|
+
content: toolResultContent,
|
|
1851
|
+
isError: !result.success,
|
|
1852
|
+
},
|
|
1853
|
+
],
|
|
1854
|
+
},
|
|
1855
|
+
skipped: false,
|
|
1856
|
+
aborted: false,
|
|
1857
|
+
};
|
|
1858
|
+
};
|
|
1859
|
+
// Execute tools - parallel if all are parallel-safe, otherwise sequential
|
|
1860
|
+
if (canParallelize) {
|
|
1861
|
+
// Parallel execution
|
|
1862
|
+
const results = await Promise.all(toolUses.map((tu) => executeSingleTool(tu)));
|
|
1863
|
+
for (let i = 0; i < toolUses.length; i++) {
|
|
1864
|
+
const toolUse = toolUses[i];
|
|
1865
|
+
const { result, toolResultMsg, aborted: wasAborted } = results[i];
|
|
1866
|
+
if (wasAborted) {
|
|
1867
|
+
aborted = true;
|
|
1868
|
+
break;
|
|
1869
|
+
}
|
|
1870
|
+
// Tool loop detection (still applies per-tool)
|
|
1871
|
+
if (this.maxConsecutiveToolCalls > 0) {
|
|
1872
|
+
const currentHash = hashToolCall(toolUse.name, toolUse.input);
|
|
1873
|
+
if (currentHash === lastToolCallHash) {
|
|
1874
|
+
consecutiveIdenticalCalls++;
|
|
1875
|
+
if (consecutiveIdenticalCalls >= this.maxConsecutiveToolCalls) {
|
|
1876
|
+
throw new ToolLoopError(toolUse.name, consecutiveIdenticalCalls, toolUse.input);
|
|
1877
|
+
}
|
|
1833
1878
|
emit({
|
|
1834
|
-
type: '
|
|
1835
|
-
|
|
1836
|
-
|
|
1879
|
+
type: 'tool_loop_warning',
|
|
1880
|
+
toolName: toolUse.name,
|
|
1881
|
+
consecutiveCalls: consecutiveIdenticalCalls,
|
|
1837
1882
|
});
|
|
1838
1883
|
}
|
|
1839
|
-
else
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1884
|
+
else {
|
|
1885
|
+
lastToolCallHash = currentHash;
|
|
1886
|
+
consecutiveIdenticalCalls = 1;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const toolCallEntry = { name: toolUse.name, input: toolUse.input, result };
|
|
1890
|
+
toolCalls.push(toolCallEntry);
|
|
1891
|
+
iterationToolCalls.push(toolCallEntry);
|
|
1892
|
+
messages.push(toolResultMsg);
|
|
1893
|
+
newMessages.push(toolResultMsg);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
else {
|
|
1897
|
+
// Sequential execution (original loop, but using the helper)
|
|
1898
|
+
for (const toolUse of toolUses) {
|
|
1899
|
+
const { result, toolResultMsg, skipped, aborted: wasAborted, } = await executeSingleTool(toolUse);
|
|
1900
|
+
if (wasAborted) {
|
|
1901
|
+
aborted = true;
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
// Tool loop detection
|
|
1905
|
+
if (this.maxConsecutiveToolCalls > 0) {
|
|
1906
|
+
const currentHash = hashToolCall(toolUse.name, toolUse.input);
|
|
1907
|
+
if (currentHash === lastToolCallHash) {
|
|
1908
|
+
consecutiveIdenticalCalls++;
|
|
1909
|
+
if (consecutiveIdenticalCalls >= this.maxConsecutiveToolCalls) {
|
|
1910
|
+
throw new ToolLoopError(toolUse.name, consecutiveIdenticalCalls, toolUse.input);
|
|
1911
|
+
}
|
|
1845
1912
|
emit({
|
|
1846
|
-
type: '
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
rounds: sumResult.rounds,
|
|
1913
|
+
type: 'tool_loop_warning',
|
|
1914
|
+
toolName: toolUse.name,
|
|
1915
|
+
consecutiveCalls: consecutiveIdenticalCalls,
|
|
1850
1916
|
});
|
|
1851
1917
|
}
|
|
1918
|
+
else {
|
|
1919
|
+
lastToolCallHash = currentHash;
|
|
1920
|
+
consecutiveIdenticalCalls = 1;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const toolCallEntry = { name: toolUse.name, input: toolUse.input, result };
|
|
1924
|
+
toolCalls.push(toolCallEntry);
|
|
1925
|
+
iterationToolCalls.push(toolCallEntry);
|
|
1926
|
+
messages.push(toolResultMsg);
|
|
1927
|
+
newMessages.push(toolResultMsg);
|
|
1928
|
+
if (skipped) {
|
|
1929
|
+
continue;
|
|
1852
1930
|
}
|
|
1853
|
-
// Track tool result tokens
|
|
1854
|
-
const finalTokens = this.contextManager.estimateTokens(toolResultContent);
|
|
1855
|
-
this.contextManager.addToCategory('toolResults', finalTokens);
|
|
1856
1931
|
}
|
|
1857
|
-
const toolResultMsg = {
|
|
1858
|
-
role: 'user',
|
|
1859
|
-
content: [
|
|
1860
|
-
{
|
|
1861
|
-
type: 'tool_result',
|
|
1862
|
-
toolUseId: toolUse.id,
|
|
1863
|
-
content: toolResultContent,
|
|
1864
|
-
isError: !result.success,
|
|
1865
|
-
},
|
|
1866
|
-
],
|
|
1867
|
-
};
|
|
1868
|
-
messages.push(toolResultMsg);
|
|
1869
|
-
newMessages.push(toolResultMsg);
|
|
1870
1932
|
}
|
|
1871
1933
|
if (aborted) {
|
|
1872
1934
|
break;
|
|
@@ -1904,7 +1966,11 @@ export class Agent {
|
|
|
1904
1966
|
addedIterations: result,
|
|
1905
1967
|
});
|
|
1906
1968
|
}
|
|
1907
|
-
|
|
1969
|
+
else {
|
|
1970
|
+
// User chose to stop - set aborted to prevent error throw
|
|
1971
|
+
aborted = true;
|
|
1972
|
+
emit({ type: 'done', response: finalResponse });
|
|
1973
|
+
}
|
|
1908
1974
|
}
|
|
1909
1975
|
}
|
|
1910
1976
|
// Check if we hit max iterations without completing
|
|
@@ -2083,14 +2149,36 @@ export class Agent {
|
|
|
2083
2149
|
/**
|
|
2084
2150
|
* Generate a summary of messages using the LLM provider.
|
|
2085
2151
|
* Used for context summarization when approaching limits.
|
|
2152
|
+
*
|
|
2153
|
+
* If anchors are configured, the summary will prioritize keeping
|
|
2154
|
+
* information related to anchor content (anchor-weighted summarization).
|
|
2086
2155
|
*/
|
|
2087
2156
|
async generateSummary(messages) {
|
|
2157
|
+
// Build anchor context if available (for weighted summarization)
|
|
2158
|
+
let anchorContext = '';
|
|
2159
|
+
if (this.anchorManager && this.anchorManager.size > 0) {
|
|
2160
|
+
const anchors = this.anchorManager.getAll();
|
|
2161
|
+
if (anchors.length > 0) {
|
|
2162
|
+
const anchorItems = anchors.map((a) => `- [${a.priority}] ${a.content}`).join('\n');
|
|
2163
|
+
anchorContext = `
|
|
2164
|
+
## PRIORITY CONTEXT (Anchors)
|
|
2165
|
+
|
|
2166
|
+
The following are critical anchors that should be preserved in context. When summarizing,
|
|
2167
|
+
PRIORITIZE keeping information that relates to these anchors:
|
|
2168
|
+
|
|
2169
|
+
${anchorItems}
|
|
2170
|
+
|
|
2171
|
+
Ensure the summary captures any conversation details related to these anchors.
|
|
2172
|
+
`;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2088
2175
|
const summaryPrompt = `Please provide a concise summary of the following conversation. Focus on:
|
|
2089
2176
|
1. Key decisions made
|
|
2090
2177
|
2. Important context established
|
|
2091
2178
|
3. Tasks completed or in progress
|
|
2092
2179
|
4. Any relevant file paths or code snippets mentioned
|
|
2093
|
-
|
|
2180
|
+
${anchorContext ? '5. Information related to the priority anchors listed below' : ''}
|
|
2181
|
+
${anchorContext}
|
|
2094
2182
|
Keep the summary under 500 words.
|
|
2095
2183
|
|
|
2096
2184
|
Conversation to summarize:
|
|
@@ -66,6 +66,40 @@ export declare class AnchorManager {
|
|
|
66
66
|
* Clear anchors based on criteria
|
|
67
67
|
*/
|
|
68
68
|
clear(options?: AnchorClearOptions): number;
|
|
69
|
+
/**
|
|
70
|
+
* Get all anchors for a specific project
|
|
71
|
+
*
|
|
72
|
+
* @param projectId - The project ID to filter by
|
|
73
|
+
* @param options - Additional query options (priority, scope, tags)
|
|
74
|
+
*/
|
|
75
|
+
getByProject(projectId: string, options?: Omit<AnchorQueryOptions, 'projectId' | 'globalOnly'>): Anchor[];
|
|
76
|
+
/**
|
|
77
|
+
* Get all global anchors (those without a projectId)
|
|
78
|
+
*
|
|
79
|
+
* @param options - Additional query options (priority, scope, tags)
|
|
80
|
+
*/
|
|
81
|
+
getGlobal(options?: Omit<AnchorQueryOptions, 'projectId' | 'globalOnly'>): Anchor[];
|
|
82
|
+
/**
|
|
83
|
+
* Clear all anchors for a specific project
|
|
84
|
+
*
|
|
85
|
+
* @param projectId - The project ID
|
|
86
|
+
* @returns Number of anchors removed
|
|
87
|
+
*/
|
|
88
|
+
clearByProject(projectId: string): number;
|
|
89
|
+
/**
|
|
90
|
+
* Get list of unique project IDs that have anchors
|
|
91
|
+
*/
|
|
92
|
+
getProjectIds(): string[];
|
|
93
|
+
/**
|
|
94
|
+
* Check if a project has any anchors
|
|
95
|
+
*/
|
|
96
|
+
hasProjectAnchors(projectId: string): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Get anchor counts grouped by project
|
|
99
|
+
*
|
|
100
|
+
* @returns Map where keys are project IDs (null for global) and values are counts
|
|
101
|
+
*/
|
|
102
|
+
getProjectStats(): Map<string | null, number>;
|
|
69
103
|
/**
|
|
70
104
|
* Get the current number of anchors
|
|
71
105
|
*/
|
package/dist/anchors/manager.js
CHANGED
|
@@ -104,6 +104,7 @@ export class AnchorManager {
|
|
|
104
104
|
expiresAt: input.expiresAt,
|
|
105
105
|
tags: input.tags,
|
|
106
106
|
metadata: input.metadata,
|
|
107
|
+
projectId: input.projectId,
|
|
107
108
|
};
|
|
108
109
|
// Store the anchor
|
|
109
110
|
this.anchors.set(anchor.id, anchor);
|
|
@@ -149,6 +150,14 @@ export class AnchorManager {
|
|
|
149
150
|
return tagsFilter.every((tag) => anchorTags.includes(tag));
|
|
150
151
|
});
|
|
151
152
|
}
|
|
153
|
+
// Filter by project ID
|
|
154
|
+
if (options?.projectId) {
|
|
155
|
+
anchors = anchors.filter((a) => a.projectId === options.projectId);
|
|
156
|
+
}
|
|
157
|
+
// Filter to global only (no projectId)
|
|
158
|
+
if (options?.globalOnly) {
|
|
159
|
+
anchors = anchors.filter((a) => !a.projectId);
|
|
160
|
+
}
|
|
152
161
|
// Filter out expired unless requested
|
|
153
162
|
if (!options?.includeExpired) {
|
|
154
163
|
anchors = anchors.filter((a) => !this.isExpired(a));
|
|
@@ -204,6 +213,14 @@ export class AnchorManager {
|
|
|
204
213
|
if (options?.expiredOnly && !this.isExpired(anchor)) {
|
|
205
214
|
continue;
|
|
206
215
|
}
|
|
216
|
+
// Filter by project ID
|
|
217
|
+
if (options?.projectId && anchor.projectId !== options.projectId) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
// Filter to global only (no projectId)
|
|
221
|
+
if (options?.globalOnly && anchor.projectId) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
207
224
|
toRemove.push(id);
|
|
208
225
|
}
|
|
209
226
|
for (const id of toRemove) {
|
|
@@ -212,6 +229,74 @@ export class AnchorManager {
|
|
|
212
229
|
}
|
|
213
230
|
return removed;
|
|
214
231
|
}
|
|
232
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
233
|
+
// Project-scoped methods
|
|
234
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
235
|
+
/**
|
|
236
|
+
* Get all anchors for a specific project
|
|
237
|
+
*
|
|
238
|
+
* @param projectId - The project ID to filter by
|
|
239
|
+
* @param options - Additional query options (priority, scope, tags)
|
|
240
|
+
*/
|
|
241
|
+
getByProject(projectId, options) {
|
|
242
|
+
return this.getAll({ ...options, projectId });
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get all global anchors (those without a projectId)
|
|
246
|
+
*
|
|
247
|
+
* @param options - Additional query options (priority, scope, tags)
|
|
248
|
+
*/
|
|
249
|
+
getGlobal(options) {
|
|
250
|
+
return this.getAll({ ...options, globalOnly: true });
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Clear all anchors for a specific project
|
|
254
|
+
*
|
|
255
|
+
* @param projectId - The project ID
|
|
256
|
+
* @returns Number of anchors removed
|
|
257
|
+
*/
|
|
258
|
+
clearByProject(projectId) {
|
|
259
|
+
return this.clear({ projectId });
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Get list of unique project IDs that have anchors
|
|
263
|
+
*/
|
|
264
|
+
getProjectIds() {
|
|
265
|
+
const projectIds = new Set();
|
|
266
|
+
for (const anchor of this.anchors.values()) {
|
|
267
|
+
if (anchor.projectId && !this.isExpired(anchor)) {
|
|
268
|
+
projectIds.add(anchor.projectId);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return Array.from(projectIds);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Check if a project has any anchors
|
|
275
|
+
*/
|
|
276
|
+
hasProjectAnchors(projectId) {
|
|
277
|
+
for (const anchor of this.anchors.values()) {
|
|
278
|
+
if (anchor.projectId === projectId && !this.isExpired(anchor)) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get anchor counts grouped by project
|
|
286
|
+
*
|
|
287
|
+
* @returns Map where keys are project IDs (null for global) and values are counts
|
|
288
|
+
*/
|
|
289
|
+
getProjectStats() {
|
|
290
|
+
const stats = new Map();
|
|
291
|
+
for (const anchor of this.anchors.values()) {
|
|
292
|
+
if (this.isExpired(anchor))
|
|
293
|
+
continue;
|
|
294
|
+
const key = anchor.projectId ?? null;
|
|
295
|
+
stats.set(key, (stats.get(key) ?? 0) + 1);
|
|
296
|
+
}
|
|
297
|
+
return stats;
|
|
298
|
+
}
|
|
299
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
215
300
|
/**
|
|
216
301
|
* Get the current number of anchors
|
|
217
302
|
*/
|
|
@@ -391,6 +476,7 @@ export class AnchorManager {
|
|
|
391
476
|
expiresAt: a.expiresAt?.toISOString(),
|
|
392
477
|
tags: a.tags,
|
|
393
478
|
metadata: a.metadata,
|
|
479
|
+
projectId: a.projectId,
|
|
394
480
|
}));
|
|
395
481
|
fs.writeFileSync(expandedPath, JSON.stringify(serialized, null, 2));
|
|
396
482
|
this.emit('anchor:persisted', undefined, `Saved ${String(serialized.length)} persistent anchors`);
|