opencandle 0.1.1 → 0.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/README.md +13 -4
- package/dist/analysts/contracts.d.ts +31 -0
- package/dist/analysts/contracts.js +158 -0
- package/dist/analysts/contracts.js.map +1 -0
- package/dist/analysts/orchestrator.d.ts +11 -2
- package/dist/analysts/orchestrator.js +155 -7
- package/dist/analysts/orchestrator.js.map +1 -1
- package/dist/cli.js +26 -10
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/cache.d.ts +30 -0
- package/dist/infra/cache.js +40 -3
- package/dist/infra/cache.js.map +1 -1
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/index.js +1 -1
- package/dist/infra/index.js.map +1 -1
- package/dist/infra/opencandle-paths.d.ts +1 -0
- package/dist/infra/opencandle-paths.js +3 -0
- package/dist/infra/opencandle-paths.js.map +1 -1
- package/dist/infra/rate-limiter.js +1 -0
- package/dist/infra/rate-limiter.js.map +1 -1
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/manager.d.ts +19 -0
- package/dist/memory/manager.js +132 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/sqlite.js +12 -1
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/memory/types.d.ts +21 -0
- package/dist/memory/types.js +47 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/pi/opencandle-extension.d.ts +5 -1
- package/dist/pi/opencandle-extension.js +45 -142
- package/dist/pi/opencandle-extension.js.map +1 -1
- package/dist/pi/session.d.ts +2 -0
- package/dist/pi/session.js +1 -1
- package/dist/pi/session.js.map +1 -1
- package/dist/prompts/context-builder.d.ts +26 -0
- package/dist/prompts/context-builder.js +126 -0
- package/dist/prompts/context-builder.js.map +1 -0
- package/dist/prompts/sections.d.ts +13 -0
- package/dist/prompts/sections.js +35 -0
- package/dist/prompts/sections.js.map +1 -0
- package/dist/providers/alpha-vantage.d.ts +3 -0
- package/dist/providers/alpha-vantage.js +184 -76
- package/dist/providers/alpha-vantage.js.map +1 -1
- package/dist/providers/coingecko.js +53 -37
- package/dist/providers/coingecko.js.map +1 -1
- package/dist/providers/fear-greed.js +23 -15
- package/dist/providers/fear-greed.js.map +1 -1
- package/dist/providers/fred.js +35 -27
- package/dist/providers/fred.js.map +1 -1
- package/dist/providers/reddit.js +44 -36
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/twitter.d.ts +20 -0
- package/dist/providers/twitter.js +143 -0
- package/dist/providers/twitter.js.map +1 -0
- package/dist/providers/with-fallback.d.ts +15 -0
- package/dist/providers/with-fallback.js +32 -0
- package/dist/providers/with-fallback.js.map +1 -0
- package/dist/providers/wrap-provider.d.ts +13 -0
- package/dist/providers/wrap-provider.js +43 -0
- package/dist/providers/wrap-provider.js.map +1 -0
- package/dist/providers/yahoo-finance.js +77 -57
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/runtime/evidence.d.ts +35 -0
- package/dist/runtime/evidence.js +29 -0
- package/dist/runtime/evidence.js.map +1 -0
- package/dist/runtime/index.d.ts +16 -0
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/prompt-step.d.ts +41 -0
- package/dist/runtime/prompt-step.js +42 -0
- package/dist/runtime/prompt-step.js.map +1 -0
- package/dist/runtime/provider-ids.d.ts +14 -0
- package/dist/runtime/provider-ids.js +14 -0
- package/dist/runtime/provider-ids.js.map +1 -0
- package/dist/runtime/provider-tracker.d.ts +20 -0
- package/dist/runtime/provider-tracker.js +36 -0
- package/dist/runtime/provider-tracker.js.map +1 -0
- package/dist/runtime/run-context.d.ts +11 -0
- package/dist/runtime/run-context.js +14 -0
- package/dist/runtime/run-context.js.map +1 -0
- package/dist/runtime/session-coordinator.d.ts +48 -0
- package/dist/runtime/session-coordinator.js +171 -0
- package/dist/runtime/session-coordinator.js.map +1 -0
- package/dist/runtime/validation.d.ts +44 -0
- package/dist/runtime/validation.js +157 -0
- package/dist/runtime/validation.js.map +1 -0
- package/dist/runtime/workflow-events.d.ts +21 -0
- package/dist/runtime/workflow-events.js +31 -0
- package/dist/runtime/workflow-events.js.map +1 -0
- package/dist/runtime/workflow-runner.d.ts +36 -0
- package/dist/runtime/workflow-runner.js +129 -0
- package/dist/runtime/workflow-runner.js.map +1 -0
- package/dist/runtime/workflow-types.d.ts +60 -0
- package/dist/runtime/workflow-types.js +32 -0
- package/dist/runtime/workflow-types.js.map +1 -0
- package/dist/system-prompt.js +52 -2
- package/dist/system-prompt.js.map +1 -1
- package/dist/tool-kit.d.ts +9 -5
- package/dist/tool-kit.js +29 -6
- package/dist/tool-kit.js.map +1 -1
- package/dist/tools/fundamentals/company-overview.js +13 -2
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +15 -7
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +23 -5
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.js +9 -1
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.js +9 -1
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.js +9 -1
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interaction/ask-user.d.ts +3 -0
- package/dist/tools/interaction/ask-user.js +87 -0
- package/dist/tools/interaction/ask-user.js.map +1 -0
- package/dist/tools/interaction/twitter-login.d.ts +8 -0
- package/dist/tools/interaction/twitter-login.js +77 -0
- package/dist/tools/interaction/twitter-login.js.map +1 -0
- package/dist/tools/macro/fear-greed.js +9 -1
- package/dist/tools/macro/fear-greed.js.map +1 -1
- package/dist/tools/macro/fred-data.js +13 -2
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +9 -1
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +9 -1
- package/dist/tools/market/crypto-price.js.map +1 -1
- package/dist/tools/market/stock-history.js +28 -1
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +29 -4
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/option-chain.js +9 -1
- package/dist/tools/options/option-chain.js.map +1 -1
- package/dist/tools/portfolio/correlation.js +15 -3
- package/dist/tools/portfolio/correlation.js.map +1 -1
- package/dist/tools/portfolio/predictions.js +6 -5
- package/dist/tools/portfolio/predictions.js.map +1 -1
- package/dist/tools/portfolio/risk-analysis.js +9 -1
- package/dist/tools/portfolio/risk-analysis.js.map +1 -1
- package/dist/tools/portfolio/tracker.js +6 -3
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.js +6 -1
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/news-sentiment.js +8 -10
- package/dist/tools/sentiment/news-sentiment.js.map +1 -1
- package/dist/tools/sentiment/reddit-sentiment.js +9 -1
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/twitter-sentiment.d.ts +9 -0
- package/dist/tools/sentiment/twitter-sentiment.js +58 -0
- package/dist/tools/sentiment/twitter-sentiment.js.map +1 -0
- package/dist/tools/technical/backtest.js +9 -1
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +9 -1
- package/dist/tools/technical/indicators.js.map +1 -1
- package/dist/types/index.d.ts +15 -0
- package/dist/types/sentiment.d.ts +20 -0
- package/dist/workflows/compare-assets.d.ts +3 -0
- package/dist/workflows/compare-assets.js +21 -5
- package/dist/workflows/compare-assets.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -3
- package/dist/workflows/index.js +3 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/options-screener.d.ts +3 -0
- package/dist/workflows/options-screener.js +24 -7
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +3 -0
- package/dist/workflows/portfolio-builder.js +30 -9
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/package.json +9 -5
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/** Create an empty validation result. */
|
|
2
|
+
export function emptyValidationResult() {
|
|
3
|
+
return { passes: [], failures: [], warnings: [] };
|
|
4
|
+
}
|
|
5
|
+
/** Check that market-sensitive evidence records have timestamps. */
|
|
6
|
+
export function checkTimestamps(evidence, marketSensitiveLabels) {
|
|
7
|
+
const warnings = [];
|
|
8
|
+
for (const record of evidence) {
|
|
9
|
+
if (marketSensitiveLabels.has(record.label) &&
|
|
10
|
+
record.provenance.source === "fetched" &&
|
|
11
|
+
!record.provenance.timestamp) {
|
|
12
|
+
warnings.push({
|
|
13
|
+
message: `Market-sensitive value '${record.label}' has no timestamp`,
|
|
14
|
+
evidenceLabel: record.label,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return warnings;
|
|
19
|
+
}
|
|
20
|
+
/** Check that options expiry dates are in the future. */
|
|
21
|
+
export function checkOptionsExpiries(evidence, today) {
|
|
22
|
+
const failures = [];
|
|
23
|
+
for (const record of evidence) {
|
|
24
|
+
if (record.label.toLowerCase().includes("expir") &&
|
|
25
|
+
typeof record.value === "string" &&
|
|
26
|
+
record.value < today) {
|
|
27
|
+
failures.push({
|
|
28
|
+
message: `Options expiry ${record.value} is in the past`,
|
|
29
|
+
evidenceLabel: record.label,
|
|
30
|
+
detail: `Today is ${today}`,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return failures;
|
|
35
|
+
}
|
|
36
|
+
/** Check that all required fields have evidence records. */
|
|
37
|
+
export function checkRequiredFields(evidence, requiredLabels) {
|
|
38
|
+
const present = new Set(evidence.map((e) => e.label));
|
|
39
|
+
const failures = [];
|
|
40
|
+
for (const label of requiredLabels) {
|
|
41
|
+
if (!present.has(label)) {
|
|
42
|
+
failures.push({
|
|
43
|
+
message: `Required field '${label}' has no evidence record`,
|
|
44
|
+
evidenceLabel: label,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return failures;
|
|
49
|
+
}
|
|
50
|
+
/** Default market-sensitive labels. */
|
|
51
|
+
export const DEFAULT_MARKET_SENSITIVE_LABELS = new Set([
|
|
52
|
+
"Stock Price",
|
|
53
|
+
"Volume",
|
|
54
|
+
"Market Cap",
|
|
55
|
+
"52-Week High",
|
|
56
|
+
"52-Week Low",
|
|
57
|
+
"Bid",
|
|
58
|
+
"Ask",
|
|
59
|
+
"Day High",
|
|
60
|
+
"Day Low",
|
|
61
|
+
"Open",
|
|
62
|
+
"Previous Close",
|
|
63
|
+
"Crypto Price",
|
|
64
|
+
"Crypto Volume",
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Orchestrates all deterministic validation checks on evidence records.
|
|
68
|
+
* Runs before LLM-based validation.
|
|
69
|
+
*/
|
|
70
|
+
export class RuntimeValidator {
|
|
71
|
+
config;
|
|
72
|
+
constructor(config = {}) {
|
|
73
|
+
this.config = config;
|
|
74
|
+
}
|
|
75
|
+
/** Run all validation checks and return a combined result. */
|
|
76
|
+
validate(evidence) {
|
|
77
|
+
const result = emptyValidationResult();
|
|
78
|
+
// Timestamp checks
|
|
79
|
+
const timestampWarnings = checkTimestamps(evidence, this.config.marketSensitiveLabels ?? DEFAULT_MARKET_SENSITIVE_LABELS);
|
|
80
|
+
result.warnings.push(...timestampWarnings);
|
|
81
|
+
// Options expiry checks
|
|
82
|
+
const today = this.config.today ?? new Date().toISOString().slice(0, 10);
|
|
83
|
+
const expiryFailures = checkOptionsExpiries(evidence, today);
|
|
84
|
+
result.failures.push(...expiryFailures);
|
|
85
|
+
// Required field checks
|
|
86
|
+
if (this.config.requiredFields) {
|
|
87
|
+
const fieldFailures = checkRequiredFields(evidence, this.config.requiredFields);
|
|
88
|
+
result.failures.push(...fieldFailures);
|
|
89
|
+
}
|
|
90
|
+
// Number match checks
|
|
91
|
+
if (this.config.toolResults) {
|
|
92
|
+
const numberEntries = checkNumberMatch(evidence, this.config.toolResults);
|
|
93
|
+
for (const entry of numberEntries) {
|
|
94
|
+
if (entry.type === "pass") {
|
|
95
|
+
result.passes.push(entry);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
result.failures.push(entry);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
/** Format validation results as a summary string for the LLM validation prompt. */
|
|
105
|
+
formatForLLM(result) {
|
|
106
|
+
const lines = ["## Deterministic Validation Results"];
|
|
107
|
+
if (result.failures.length > 0) {
|
|
108
|
+
lines.push(`\n### Failures (${result.failures.length})`);
|
|
109
|
+
for (const f of result.failures) {
|
|
110
|
+
lines.push(`- ${f.message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (result.warnings.length > 0) {
|
|
114
|
+
lines.push(`\n### Warnings (${result.warnings.length})`);
|
|
115
|
+
for (const w of result.warnings) {
|
|
116
|
+
lines.push(`- ${w.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (result.passes.length > 0) {
|
|
120
|
+
lines.push(`\n### Verified (${result.passes.length})`);
|
|
121
|
+
for (const p of result.passes) {
|
|
122
|
+
lines.push(`- ${p.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (result.failures.length === 0 && result.warnings.length === 0) {
|
|
126
|
+
lines.push("\nAll deterministic checks passed.");
|
|
127
|
+
}
|
|
128
|
+
return lines.join("\n");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Check that evidence values match expected tool result values. */
|
|
132
|
+
export function checkNumberMatch(evidence, toolResults) {
|
|
133
|
+
const results = [];
|
|
134
|
+
for (const record of evidence) {
|
|
135
|
+
if (typeof record.value !== "number")
|
|
136
|
+
continue;
|
|
137
|
+
const expected = toolResults.get(record.label);
|
|
138
|
+
if (expected === undefined)
|
|
139
|
+
continue;
|
|
140
|
+
if (record.value === expected) {
|
|
141
|
+
results.push({
|
|
142
|
+
type: "pass",
|
|
143
|
+
message: `${record.label}: ${record.value} matches tool result`,
|
|
144
|
+
evidenceLabel: record.label,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
results.push({
|
|
149
|
+
type: "failure",
|
|
150
|
+
message: `${record.label} mismatch: evidence says ${record.value}, tool returned ${expected}`,
|
|
151
|
+
evidenceLabel: record.label,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return results;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/runtime/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
/** All workflow event types. */
|
|
3
|
+
export type WorkflowEventType = "workflow_started" | "slot_resolved" | "clarification_asked" | "clarification_answered" | "step_started" | "step_completed" | "step_failed" | "step_skipped" | "tool_called" | "tool_failed" | "validation_passed" | "validation_failed" | "workflow_completed" | "workflow_cancelled";
|
|
4
|
+
/** A persisted workflow event row. */
|
|
5
|
+
export interface WorkflowEvent {
|
|
6
|
+
id: number;
|
|
7
|
+
runId: string;
|
|
8
|
+
stepIndex: number;
|
|
9
|
+
eventType: WorkflowEventType;
|
|
10
|
+
payloadJson: string | null;
|
|
11
|
+
timestamp: string;
|
|
12
|
+
}
|
|
13
|
+
/** Append-only workflow event logger backed by SQLite. */
|
|
14
|
+
export declare class WorkflowEventLogger {
|
|
15
|
+
private readonly db;
|
|
16
|
+
constructor(db: Database.Database);
|
|
17
|
+
/** Append a workflow event. */
|
|
18
|
+
log(runId: string, stepIndex: number, eventType: WorkflowEventType, payload?: Record<string, unknown>): void;
|
|
19
|
+
/** Query all events for a given run ID, ordered by timestamp. */
|
|
20
|
+
getEventsByRunId(runId: string): WorkflowEvent[];
|
|
21
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Append-only workflow event logger backed by SQLite. */
|
|
2
|
+
export class WorkflowEventLogger {
|
|
3
|
+
db;
|
|
4
|
+
constructor(db) {
|
|
5
|
+
this.db = db;
|
|
6
|
+
}
|
|
7
|
+
/** Append a workflow event. */
|
|
8
|
+
log(runId, stepIndex, eventType, payload) {
|
|
9
|
+
const now = new Date().toISOString();
|
|
10
|
+
const payloadJson = payload ? JSON.stringify(payload) : null;
|
|
11
|
+
this.db
|
|
12
|
+
.prepare(`INSERT INTO workflow_events (run_id, step_index, event_type, payload_json, timestamp)
|
|
13
|
+
VALUES (?, ?, ?, ?, ?)`)
|
|
14
|
+
.run(runId, stepIndex, eventType, payloadJson, now);
|
|
15
|
+
}
|
|
16
|
+
/** Query all events for a given run ID, ordered by timestamp. */
|
|
17
|
+
getEventsByRunId(runId) {
|
|
18
|
+
const rows = this.db
|
|
19
|
+
.prepare("SELECT id, run_id, step_index, event_type, payload_json, timestamp FROM workflow_events WHERE run_id = ? ORDER BY id")
|
|
20
|
+
.all(runId);
|
|
21
|
+
return rows.map((r) => ({
|
|
22
|
+
id: r.id,
|
|
23
|
+
runId: r.run_id,
|
|
24
|
+
stepIndex: r.step_index,
|
|
25
|
+
eventType: r.event_type,
|
|
26
|
+
payloadJson: r.payload_json,
|
|
27
|
+
timestamp: r.timestamp,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=workflow-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-events.js","sourceRoot":"","sources":["../../src/runtime/workflow-events.ts"],"names":[],"mappings":"AA6BA,0DAA0D;AAC1D,MAAM,OAAO,mBAAmB;IACD;IAA7B,YAA6B,EAAqB;QAArB,OAAE,GAAF,EAAE,CAAmB;IAAG,CAAC;IAEtD,+BAA+B;IAC/B,GAAG,CACD,KAAa,EACb,SAAiB,EACjB,SAA4B,EAC5B,OAAiC;QAEjC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;gCACwB,CACzB;aACA,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,iEAAiE;IACjE,gBAAgB,CAAC,KAAa;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CACN,sHAAsH,CACvH;aACA,GAAG,CAAC,KAAK,CAOR,CAAC;QAEL,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,KAAK,EAAE,CAAC,CAAC,MAAM;YACf,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,SAAS,EAAE,CAAC,CAAC,UAA+B;YAC5C,WAAW,EAAE,CAAC,CAAC,YAAY;YAC3B,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { WorkflowRun, StepOutput, WorkflowStep } from "./workflow-types.js";
|
|
2
|
+
import type { WorkflowEventLogger } from "./workflow-events.js";
|
|
3
|
+
import type { ProviderTracker } from "./provider-tracker.js";
|
|
4
|
+
import type { EvidenceRecord } from "./evidence.js";
|
|
5
|
+
/** Function that executes a single workflow step. */
|
|
6
|
+
export type StepExecutor = (step: WorkflowStep, stepIndex: number, priorEvidence: EvidenceRecord[], context: StepExecutionContext) => Promise<StepOutput>;
|
|
7
|
+
/** Context passed to step executors. */
|
|
8
|
+
export interface StepExecutionContext {
|
|
9
|
+
runId: string;
|
|
10
|
+
providerTracker: ProviderTracker;
|
|
11
|
+
}
|
|
12
|
+
/** Options for creating a WorkflowRunner. */
|
|
13
|
+
export interface WorkflowRunnerOptions {
|
|
14
|
+
eventLogger?: WorkflowEventLogger;
|
|
15
|
+
providerTracker?: ProviderTracker;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Typed workflow execution engine with run IDs, step definitions,
|
|
19
|
+
* state transitions, cancellation, and event logging.
|
|
20
|
+
*/
|
|
21
|
+
export declare class WorkflowRunner {
|
|
22
|
+
private readonly eventLogger?;
|
|
23
|
+
private readonly providerTracker?;
|
|
24
|
+
private activeRun;
|
|
25
|
+
constructor(options?: WorkflowRunnerOptions);
|
|
26
|
+
/** Get the currently active run, if any. */
|
|
27
|
+
getActiveRun(): WorkflowRun | null;
|
|
28
|
+
/**
|
|
29
|
+
* Start a new workflow run. If a run is already active, it is cancelled first.
|
|
30
|
+
*/
|
|
31
|
+
start(workflowType: string, stepDefinitions: Omit<WorkflowStep, "status">[], executor: StepExecutor): Promise<WorkflowRun>;
|
|
32
|
+
/** Cancel the active run. */
|
|
33
|
+
cancel(): void;
|
|
34
|
+
private executeSteps;
|
|
35
|
+
private logEvent;
|
|
36
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createWorkflowRun, transitionStepStatus, } from "./workflow-types.js";
|
|
2
|
+
let runCounter = 0;
|
|
3
|
+
function generateRunId() {
|
|
4
|
+
runCounter += 1;
|
|
5
|
+
return `run_${Date.now()}_${runCounter}`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Typed workflow execution engine with run IDs, step definitions,
|
|
9
|
+
* state transitions, cancellation, and event logging.
|
|
10
|
+
*/
|
|
11
|
+
export class WorkflowRunner {
|
|
12
|
+
eventLogger;
|
|
13
|
+
providerTracker;
|
|
14
|
+
activeRun = null;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.eventLogger = options.eventLogger;
|
|
17
|
+
this.providerTracker = options.providerTracker;
|
|
18
|
+
}
|
|
19
|
+
/** Get the currently active run, if any. */
|
|
20
|
+
getActiveRun() {
|
|
21
|
+
return this.activeRun;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Start a new workflow run. If a run is already active, it is cancelled first.
|
|
25
|
+
*/
|
|
26
|
+
async start(workflowType, stepDefinitions, executor) {
|
|
27
|
+
// Cancel any active run
|
|
28
|
+
if (this.activeRun && this.activeRun.status === "running") {
|
|
29
|
+
this.cancel();
|
|
30
|
+
}
|
|
31
|
+
const runId = generateRunId();
|
|
32
|
+
const run = createWorkflowRun(runId, workflowType, stepDefinitions);
|
|
33
|
+
this.activeRun = run;
|
|
34
|
+
run.status = "running";
|
|
35
|
+
this.providerTracker?.resetAll();
|
|
36
|
+
this.logEvent(runId, 0, "workflow_started", {
|
|
37
|
+
workflowType,
|
|
38
|
+
stepCount: stepDefinitions.length,
|
|
39
|
+
});
|
|
40
|
+
// Execute steps
|
|
41
|
+
await this.executeSteps(run, executor);
|
|
42
|
+
return run;
|
|
43
|
+
}
|
|
44
|
+
/** Cancel the active run. */
|
|
45
|
+
cancel() {
|
|
46
|
+
const run = this.activeRun;
|
|
47
|
+
if (!run || run.status !== "running")
|
|
48
|
+
return;
|
|
49
|
+
for (let i = run.currentStepIndex; i < run.steps.length; i++) {
|
|
50
|
+
const step = run.steps[i];
|
|
51
|
+
if (step.status === "pending" || step.status === "running") {
|
|
52
|
+
step.status = transitionStepStatus(step.status, "skipped");
|
|
53
|
+
this.logEvent(run.runId, i, "step_skipped", {
|
|
54
|
+
stepType: step.stepType,
|
|
55
|
+
reason: "cancelled",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
run.status = "cancelled";
|
|
60
|
+
this.logEvent(run.runId, run.currentStepIndex, "workflow_cancelled", {
|
|
61
|
+
cancelledAtStep: run.currentStepIndex,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async executeSteps(run, executor) {
|
|
65
|
+
for (let i = 0; i < run.steps.length; i++) {
|
|
66
|
+
// Check if run was cancelled externally
|
|
67
|
+
if (run.status !== "running")
|
|
68
|
+
return;
|
|
69
|
+
const step = run.steps[i];
|
|
70
|
+
run.currentStepIndex = i;
|
|
71
|
+
// Collect all prior evidence
|
|
72
|
+
const priorEvidence = [];
|
|
73
|
+
for (const [, output] of run.stepOutputs) {
|
|
74
|
+
priorEvidence.push(...output.evidence);
|
|
75
|
+
}
|
|
76
|
+
// Transition to running
|
|
77
|
+
step.status = transitionStepStatus(step.status, "running");
|
|
78
|
+
this.logEvent(run.runId, i, "step_started", { stepType: step.stepType });
|
|
79
|
+
try {
|
|
80
|
+
const context = {
|
|
81
|
+
runId: run.runId,
|
|
82
|
+
providerTracker: this.providerTracker,
|
|
83
|
+
};
|
|
84
|
+
const output = await executor(step, i, priorEvidence, context);
|
|
85
|
+
// If run was cancelled during execution, stop without further transitions
|
|
86
|
+
if (run.status !== "running")
|
|
87
|
+
return;
|
|
88
|
+
step.status = transitionStepStatus(step.status, "completed");
|
|
89
|
+
run.stepOutputs.set(i, output);
|
|
90
|
+
this.logEvent(run.runId, i, "step_completed", {
|
|
91
|
+
stepType: step.stepType,
|
|
92
|
+
evidenceCount: output.evidence.length,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// If run was cancelled during execution, stop without further transitions
|
|
97
|
+
if (run.status !== "running")
|
|
98
|
+
return;
|
|
99
|
+
const message = error instanceof Error ? error.message : "unknown_error";
|
|
100
|
+
if (step.skippable) {
|
|
101
|
+
step.status = transitionStepStatus(step.status, "skipped");
|
|
102
|
+
this.logEvent(run.runId, i, "step_skipped", {
|
|
103
|
+
stepType: step.stepType,
|
|
104
|
+
reason: message,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
step.status = transitionStepStatus(step.status, "failed");
|
|
109
|
+
this.logEvent(run.runId, i, "step_failed", {
|
|
110
|
+
stepType: step.stepType,
|
|
111
|
+
error: message,
|
|
112
|
+
});
|
|
113
|
+
run.status = "failed";
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (run.status === "running") {
|
|
119
|
+
run.status = "completed";
|
|
120
|
+
this.logEvent(run.runId, run.steps.length - 1, "workflow_completed", {
|
|
121
|
+
workflowType: run.workflowType,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
logEvent(runId, stepIndex, eventType, payload) {
|
|
126
|
+
this.eventLogger?.log(runId, stepIndex, eventType, payload);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=workflow-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-runner.js","sourceRoot":"","sources":["../../src/runtime/workflow-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { EvidenceRecord } from "./evidence.js";
|
|
2
|
+
/** Status of a single workflow step. */
|
|
3
|
+
export type StepStatus = "pending" | "running" | "completed" | "failed" | "skipped";
|
|
4
|
+
/** Overall status of a workflow run. */
|
|
5
|
+
export type RunStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
6
|
+
/** Check whether a step status transition is valid. */
|
|
7
|
+
export declare function isValidStepTransition(from: StepStatus, to: StepStatus): boolean;
|
|
8
|
+
/** Transition a step status, throwing on invalid transitions. */
|
|
9
|
+
export declare function transitionStepStatus(from: StepStatus, to: StepStatus): StepStatus;
|
|
10
|
+
/** Definition of a single workflow step. */
|
|
11
|
+
export interface WorkflowStep {
|
|
12
|
+
stepType: string;
|
|
13
|
+
description: string;
|
|
14
|
+
requiredInputs: string[];
|
|
15
|
+
expectedOutputs: string[];
|
|
16
|
+
skippable: boolean;
|
|
17
|
+
status: StepStatus;
|
|
18
|
+
}
|
|
19
|
+
/** Output produced by a completed workflow step. */
|
|
20
|
+
export interface StepOutput {
|
|
21
|
+
stepIndex: number;
|
|
22
|
+
stepType: string;
|
|
23
|
+
evidence: EvidenceRecord[];
|
|
24
|
+
rawText?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Analyst signal direction. */
|
|
27
|
+
export type AnalystSignal = "BUY" | "HOLD" | "SELL";
|
|
28
|
+
/** Structured output from a single analyst role. */
|
|
29
|
+
export interface AnalystOutput {
|
|
30
|
+
role: string;
|
|
31
|
+
signal: AnalystSignal;
|
|
32
|
+
conviction: number;
|
|
33
|
+
thesis: string;
|
|
34
|
+
evidence: EvidenceRecord[];
|
|
35
|
+
rawText?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Debate side in bull/bear adversarial debate. */
|
|
38
|
+
export type DebateSide = "bull" | "bear";
|
|
39
|
+
/** Structured output from a debate step (eval/test only — not used in live path). */
|
|
40
|
+
export interface DebateOutput {
|
|
41
|
+
side: DebateSide;
|
|
42
|
+
thesis: string;
|
|
43
|
+
keyRisk: string;
|
|
44
|
+
concessions: string[];
|
|
45
|
+
remainingConviction: number;
|
|
46
|
+
evidence: EvidenceRecord[];
|
|
47
|
+
rawText: string;
|
|
48
|
+
}
|
|
49
|
+
/** A complete workflow run definition and state. */
|
|
50
|
+
export interface WorkflowRun {
|
|
51
|
+
runId: string;
|
|
52
|
+
workflowType: string;
|
|
53
|
+
steps: WorkflowStep[];
|
|
54
|
+
currentStepIndex: number;
|
|
55
|
+
status: RunStatus;
|
|
56
|
+
stepOutputs: Map<number, StepOutput>;
|
|
57
|
+
createdAt: string;
|
|
58
|
+
}
|
|
59
|
+
/** Create a new workflow run with all steps in pending state. */
|
|
60
|
+
export declare function createWorkflowRun(runId: string, workflowType: string, stepDefinitions: Omit<WorkflowStep, "status">[]): WorkflowRun;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Valid step status transitions. */
|
|
2
|
+
const VALID_STEP_TRANSITIONS = {
|
|
3
|
+
pending: ["running", "skipped"],
|
|
4
|
+
running: ["completed", "failed", "skipped"],
|
|
5
|
+
completed: [],
|
|
6
|
+
failed: [],
|
|
7
|
+
skipped: [],
|
|
8
|
+
};
|
|
9
|
+
/** Check whether a step status transition is valid. */
|
|
10
|
+
export function isValidStepTransition(from, to) {
|
|
11
|
+
return VALID_STEP_TRANSITIONS[from].includes(to);
|
|
12
|
+
}
|
|
13
|
+
/** Transition a step status, throwing on invalid transitions. */
|
|
14
|
+
export function transitionStepStatus(from, to) {
|
|
15
|
+
if (!isValidStepTransition(from, to)) {
|
|
16
|
+
throw new Error(`Invalid step transition: ${from} → ${to}`);
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
}
|
|
20
|
+
/** Create a new workflow run with all steps in pending state. */
|
|
21
|
+
export function createWorkflowRun(runId, workflowType, stepDefinitions) {
|
|
22
|
+
return {
|
|
23
|
+
runId,
|
|
24
|
+
workflowType,
|
|
25
|
+
steps: stepDefinitions.map((def) => ({ ...def, status: "pending" })),
|
|
26
|
+
currentStepIndex: 0,
|
|
27
|
+
status: "pending",
|
|
28
|
+
stepOutputs: new Map(),
|
|
29
|
+
createdAt: new Date().toISOString(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=workflow-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-types.js","sourceRoot":"","sources":["../../src/runtime/workflow-types.ts"],"names":[],"mappings":"AAQA,qCAAqC;AACrC,MAAM,sBAAsB,GAAqC;IAC/D,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC;IAC/B,OAAO,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC;IAC3C,SAAS,EAAE,EAAE;IACb,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,uDAAuD;AACvD,MAAM,UAAU,qBAAqB,CAAC,IAAgB,EAAE,EAAc;IACpE,OAAO,sBAAsB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,oBAAoB,CAAC,IAAgB,EAAE,EAAc;IACnE,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AA0DD,iEAAiE;AACjE,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,YAAoB,EACpB,eAA+C;IAE/C,OAAO;QACL,KAAK;QACL,YAAY;QACZ,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,SAAkB,EAAE,CAAC,CAAC;QAC7E,gBAAgB,EAAE,CAAC;QACnB,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;AACJ,CAAC"}
|
package/dist/system-prompt.js
CHANGED
|
@@ -16,9 +16,10 @@ You provide data-driven analysis for stocks, crypto, macro economics, and portfo
|
|
|
16
16
|
- **Fundamentals**: get_company_overview, get_financials, get_earnings, compute_dcf, compare_companies, get_sec_filings — company financials, valuation metrics, DCF intrinsic value, peer comparison, and SEC EDGAR filings (10-K, 10-Q, 8-K)
|
|
17
17
|
- **Technical Analysis**: get_technical_indicators, backtest_strategy — SMA, EMA, RSI, MACD, Bollinger Bands, OBV, VWAP computed from price data, plus simple strategy backtesting
|
|
18
18
|
- **Macro**: get_economic_data, get_fear_greed — FRED economic indicators and market sentiment
|
|
19
|
-
- **Sentiment**: get_reddit_sentiment, get_reddit_discussions — retail sentiment from
|
|
19
|
+
- **Sentiment**: get_reddit_sentiment, get_reddit_discussions, get_twitter_sentiment — retail sentiment from Reddit and Twitter/X
|
|
20
20
|
- **Options**: get_option_chain — full options chain with strikes, bids/asks, volume, OI, IV, and computed Greeks (delta, gamma, theta, vega, rho)
|
|
21
21
|
- **Portfolio**: track_portfolio, analyze_risk, manage_watchlist, analyze_correlation, track_prediction — position tracking, P&L, Sharpe ratio, VaR, watchlist with price alerts, correlation matrix, and prediction tracking with accuracy scoring
|
|
22
|
+
- **User Interaction**: ask_user — ask clarification questions; trigger_twitter_login — open a browser for Twitter/X login
|
|
22
23
|
|
|
23
24
|
## Analytical Framework
|
|
24
25
|
When analyzing a stock, follow these steps in order:
|
|
@@ -29,7 +30,7 @@ When analyzing a stock, follow these steps in order:
|
|
|
29
30
|
5. **SYNTHESIS**: State your reasoning chain explicitly: "Because [data point] + [data point], I conclude [thesis]."
|
|
30
31
|
|
|
31
32
|
## Guidelines
|
|
32
|
-
- Always fetch data with tools before stating prices, ratios, or metrics. Never guess financial numbers.
|
|
33
|
+
- Always fetch data with tools before stating prices, ratios, or metrics. Never guess financial numbers. Every substantive response should be backed by at least one tool call — if you find yourself writing a response with zero tool calls, stop and think about what data would make it better.
|
|
33
34
|
- For options analysis, use get_option_chain to see the full chain with Greeks. Pay attention to put/call ratio, unusual volume, and IV levels.
|
|
34
35
|
- Present numerical data in tables when comparing multiple securities.
|
|
35
36
|
- Include data timestamps so users know how fresh the information is.
|
|
@@ -39,6 +40,55 @@ When analyzing a stock, follow these steps in order:
|
|
|
39
40
|
- Reuse prior tool outputs when they already answer the question. Do not re-fetch the same symbol and parameters unless you need a missing field or fresher timestamp.
|
|
40
41
|
- If one provider is missing data, continue with the remaining tools and clearly label unavailable metrics instead of aborting the entire response.
|
|
41
42
|
|
|
43
|
+
## When to Ask for Clarification
|
|
44
|
+
Use the ask_user tool BEFORE proceeding when:
|
|
45
|
+
- The request is broad or vague (e.g., "analyze the market" without specifying which asset or sector)
|
|
46
|
+
- Required information is missing: a ticker symbol for asset analysis, a budget for portfolio construction, or a time horizon for recommendations
|
|
47
|
+
- Multiple valid analysis approaches exist and the user has not indicated a preference (e.g., fundamental vs. technical, short-term vs. long-term)
|
|
48
|
+
- Risk tolerance is unclear for portfolio or options recommendations
|
|
49
|
+
|
|
50
|
+
Do NOT ask clarifying questions when:
|
|
51
|
+
- The request is clear and specific (e.g., "get AAPL quote", "analyze BTC")
|
|
52
|
+
- You can reasonably infer the intent from context or prior conversation
|
|
53
|
+
- A reasonable default exists and can be disclosed in the Assumptions block instead
|
|
54
|
+
- The user explicitly asks you to use your judgment
|
|
55
|
+
|
|
56
|
+
Keep questions concise and offer specific options when possible. Prefer select-type questions over open-ended text input to minimize user effort.
|
|
57
|
+
|
|
58
|
+
## Twitter Authentication
|
|
59
|
+
get_twitter_sentiment requires a one-time Twitter/X login. When the tool returns [LOGIN_NEEDED]:
|
|
60
|
+
1. Use ask_user (confirm) to ask: "Twitter sentiment requires a one-time login. A browser will open — want to proceed?"
|
|
61
|
+
2. If confirmed, call trigger_twitter_login. It opens a browser, waits for the user to log in, and returns success/failure.
|
|
62
|
+
3. On success, retry get_twitter_sentiment with the original query.
|
|
63
|
+
If the user declines, skip Twitter sentiment and continue with other available data sources.
|
|
64
|
+
|
|
65
|
+
## After Clarification: Fetch Data Immediately
|
|
66
|
+
CRITICAL: After ask_user answers come back, your NEXT action MUST be tool calls — not a text response. You are a data agent, not a chatbot. Never respond with generic investment categories or tell the user to come back with tickers. YOU pick the relevant assets and indicators based on what you learned, then fetch the data.
|
|
67
|
+
|
|
68
|
+
Playbooks by scenario (use these as starting points, adapt as needed):
|
|
69
|
+
|
|
70
|
+
**"Where should I put $X" / general investment advice:**
|
|
71
|
+
1. Fetch get_fear_greed — is the market fearful or greedy right now?
|
|
72
|
+
2. Fetch get_economic_data for key macro indicators (Fed funds rate, CPI, unemployment)
|
|
73
|
+
3. Fetch get_stock_quote for benchmark ETFs relevant to their goal (e.g., SPY, QQQ, VTI for growth; BND, SCHD for income; GLD, BTC for alternatives)
|
|
74
|
+
4. Fetch get_technical_indicators on those ETFs to assess current momentum and overbought/oversold conditions
|
|
75
|
+
5. Synthesize: "Given current market conditions [data], here's how I'd think about allocating $X across [specific assets] and why"
|
|
76
|
+
|
|
77
|
+
**"Build me a portfolio" / allocation request:**
|
|
78
|
+
1. Pick 5-8 candidate assets matching their stated goal and risk level
|
|
79
|
+
2. Fetch get_stock_quote and get_company_overview for each
|
|
80
|
+
3. Fetch analyze_correlation to check diversification
|
|
81
|
+
4. Present a concrete allocation with percentages, backed by the data you fetched
|
|
82
|
+
|
|
83
|
+
**"What's happening in the market" / market outlook:**
|
|
84
|
+
1. Fetch get_stock_quote for SPY, QQQ, IWM, DIA (major indices)
|
|
85
|
+
2. Fetch get_fear_greed
|
|
86
|
+
3. Fetch get_economic_data for 2-3 key FRED series
|
|
87
|
+
4. Fetch get_reddit_sentiment for current retail mood
|
|
88
|
+
5. Synthesize a market snapshot with data points
|
|
89
|
+
|
|
90
|
+
If you are about to write a response that contains zero tool call results, STOP. Go fetch data first.
|
|
91
|
+
|
|
42
92
|
## Assumption Disclosure
|
|
43
93
|
Workflow prompts include a pre-rendered "Assumptions" block with correct source attribution (user-specified, saved preference, or default). Start your response with that block exactly as written. Do NOT independently relabel any value's source anywhere in your response. The assumptions block is the single authoritative provenance representation.
|
|
44
94
|
${memorySection}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,aAAsB;IACtD,MAAM,aAAa,GAAG,aAAa;QACjC,CAAC,CAAC;;;;EAIJ,aAAa,EAAE;QACb,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO
|
|
1
|
+
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,aAAsB;IACtD,MAAM,aAAa,GAAG,aAAa;QACjC,CAAC,CAAC;;;;EAIJ,aAAa,EAAE;QACb,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqFP,aAAa;;;mNAGoM,CAAC;AACpN,CAAC"}
|
package/dist/tool-kit.d.ts
CHANGED
|
@@ -8,12 +8,16 @@ export { agentToolToPiTool } from "./pi/tool-adapter.js";
|
|
|
8
8
|
export { Type } from "@sinclair/typebox";
|
|
9
9
|
export type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
10
10
|
export type { AgentTool } from "@mariozechner/pi-agent-core";
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function getAddonToolDescriptions(): ReadonlyArray<{
|
|
12
12
|
name: string;
|
|
13
13
|
description: string;
|
|
14
14
|
}>;
|
|
15
|
-
export interface
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export interface ToolConfig<TParams extends TSchema, TDetails = unknown> {
|
|
16
|
+
name: string;
|
|
17
|
+
label: string;
|
|
18
|
+
description: string;
|
|
19
|
+
parameters: TParams;
|
|
20
|
+
execute: AgentTool<TParams, TDetails>["execute"];
|
|
18
21
|
}
|
|
19
|
-
export declare function
|
|
22
|
+
export declare function createTool<TParams extends TSchema, TDetails = unknown>(config: ToolConfig<TParams, TDetails>): AgentTool<TParams, TDetails>;
|
|
23
|
+
export declare function registerTools<TParams extends TSchema>(pi: ExtensionAPI, tools: AgentTool<TParams>[]): void;
|