agentic-lang 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/COMMUNITY.md +220 -0
- package/CONTRIBUTING.md +194 -0
- package/FINAL_REPORT.md +398 -0
- package/FOR_OTHER_LLMS.md +286 -0
- package/IMPROVEMENTS.md +319 -0
- package/LAUNCH_GUIDE.md +388 -0
- package/LICENSE +21 -0
- package/NPM_PUBLISH.md +257 -0
- package/PROJECT_COMPLETE.md +414 -0
- package/PROJECT_OVERVIEW.md +265 -0
- package/PROJECT_TREE.txt +228 -0
- package/PUBLISHING_GUIDE.md +426 -0
- package/PUBLISH_NOW.md +337 -0
- package/QUICKSTART.md +207 -0
- package/README.md +195 -0
- package/README_ENHANCED.md +329 -0
- package/READY_TO_LAUNCH.txt +56 -0
- package/REFACTOR_PLAN.md +179 -0
- package/ROADMAP.md +201 -0
- package/SUMMARY.md +315 -0
- package/bin/agentic.js +3 -0
- package/blog/001-introducing-agentic.md +382 -0
- package/blog/002-confidence-driven-development.md +490 -0
- package/blog/003-formal-verification.md +427 -0
- package/blog/004-multi-agent-production.md +436 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +151 -0
- package/dist/cli.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +115 -0
- package/dist/diagnostics/diagnostic.d.ts.map +1 -0
- package/dist/diagnostics/diagnostic.js +101 -0
- package/dist/diagnostics/diagnostic.js.map +1 -0
- package/dist/diagnostics/formatter.d.ts +36 -0
- package/dist/diagnostics/formatter.d.ts.map +1 -0
- package/dist/diagnostics/formatter.js +263 -0
- package/dist/diagnostics/formatter.js.map +1 -0
- package/dist/effects/effect-system.d.ts +64 -0
- package/dist/effects/effect-system.d.ts.map +1 -0
- package/dist/effects/effect-system.js +197 -0
- package/dist/effects/effect-system.js.map +1 -0
- package/dist/generator/typescript-generator.d.ts +31 -0
- package/dist/generator/typescript-generator.d.ts.map +1 -0
- package/dist/generator/typescript-generator.js +308 -0
- package/dist/generator/typescript-generator.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/lean4/exporter.d.ts +24 -0
- package/dist/lean4/exporter.d.ts.map +1 -0
- package/dist/lean4/exporter.js +142 -0
- package/dist/lean4/exporter.js.map +1 -0
- package/dist/lsp/server.d.ts +6 -0
- package/dist/lsp/server.d.ts.map +1 -0
- package/dist/lsp/server.js +131 -0
- package/dist/lsp/server.js.map +1 -0
- package/dist/parser/lexer.d.ts +79 -0
- package/dist/parser/lexer.d.ts.map +1 -0
- package/dist/parser/lexer.js +296 -0
- package/dist/parser/lexer.js.map +1 -0
- package/dist/parser/parser-enhanced.d.ts +12 -0
- package/dist/parser/parser-enhanced.d.ts.map +1 -0
- package/dist/parser/parser-enhanced.js +206 -0
- package/dist/parser/parser-enhanced.js.map +1 -0
- package/dist/parser/parser.d.ts +34 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +507 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/property-tests/generator-enhanced.d.ts +27 -0
- package/dist/property-tests/generator-enhanced.d.ts.map +1 -0
- package/dist/property-tests/generator-enhanced.js +209 -0
- package/dist/property-tests/generator-enhanced.js.map +1 -0
- package/dist/property-tests/generator-fixed.d.ts +2 -0
- package/dist/property-tests/generator-fixed.d.ts.map +1 -0
- package/dist/property-tests/generator-fixed.js +7 -0
- package/dist/property-tests/generator-fixed.js.map +1 -0
- package/dist/property-tests/generator.d.ts +28 -0
- package/dist/property-tests/generator.d.ts.map +1 -0
- package/dist/property-tests/generator.js +284 -0
- package/dist/property-tests/generator.js.map +1 -0
- package/dist/refinements/refinement-types.d.ts +96 -0
- package/dist/refinements/refinement-types.d.ts.map +1 -0
- package/dist/refinements/refinement-types.js +234 -0
- package/dist/refinements/refinement-types.js.map +1 -0
- package/dist/repl.d.ts +21 -0
- package/dist/repl.d.ts.map +1 -0
- package/dist/repl.js +317 -0
- package/dist/repl.js.map +1 -0
- package/dist/runtime/agents.d.ts +97 -0
- package/dist/runtime/agents.d.ts.map +1 -0
- package/dist/runtime/agents.js +258 -0
- package/dist/runtime/agents.js.map +1 -0
- package/dist/runtime/index.d.ts +98 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +253 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/types-extended.d.ts +197 -0
- package/dist/types-extended.d.ts.map +1 -0
- package/dist/types-extended.js +7 -0
- package/dist/types-extended.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/verification/z3-engine.d.ts +75 -0
- package/dist/verification/z3-engine.d.ts.map +1 -0
- package/dist/verification/z3-engine.js +234 -0
- package/dist/verification/z3-engine.js.map +1 -0
- package/examples/advanced-features.agentic +98 -0
- package/examples/annotations.agentic +37 -0
- package/examples/auth.agentic +53 -0
- package/examples/enterprise-example.agentic +360 -0
- package/examples/minimal.agentic +3 -0
- package/examples/minimal.ts +7 -0
- package/examples/ml-pipeline.agentic +350 -0
- package/examples/multi-agent-example.agentic +212 -0
- package/examples/onboarding-tutorial.agentic +263 -0
- package/examples/production-api.agentic +304 -0
- package/examples/real-world-chatbot.agentic +351 -0
- package/examples/result-handling.agentic +34 -0
- package/examples/runtime.ts +24 -0
- package/examples/showcase.agentic +22 -0
- package/examples/showcase.ts +28 -0
- package/examples/simple-test.agentic +4 -0
- package/examples/simple-test.ts +7 -0
- package/examples/simple.agentic +20 -0
- package/examples/test2.agentic +4 -0
- package/examples/test2.ts +9 -0
- package/examples/test3.agentic +4 -0
- package/examples/test3.ts +9 -0
- package/package.json +70 -0
- package/playground/index.html +221 -0
- package/playground/playground.js +291 -0
- package/registry/package-registry.ts +319 -0
- package/scripts/build.js +50 -0
- package/scripts/validate-confidence-mutation.ts +112 -0
- package/stdlib/async/promise.agentic +216 -0
- package/stdlib/database/pool.agentic +235 -0
- package/stdlib/file/io.agentic +194 -0
- package/stdlib/http/client.agentic +168 -0
- package/video-scripts/001-agentic-in-100-seconds.md +175 -0
- package/vscode-extension/README.md +67 -0
- package/vscode-extension/language-configuration.json +31 -0
- package/vscode-extension/package.json +46 -0
- package/vscode-extension/syntaxes/agentic.tmLanguage.json +134 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Database Connection Pool
|
|
2
|
+
// @agentic/db - Production-grade database access with pooling
|
|
3
|
+
|
|
4
|
+
@module("database")
|
|
5
|
+
@confidence(0.93)
|
|
6
|
+
|
|
7
|
+
type DbConfig {
|
|
8
|
+
host: string
|
|
9
|
+
port: number
|
|
10
|
+
database: string
|
|
11
|
+
user: string
|
|
12
|
+
password: string
|
|
13
|
+
minConnections: number = 2
|
|
14
|
+
maxConnections: number = 10
|
|
15
|
+
acquireTimeout: Duration = 30s
|
|
16
|
+
idleTimeout: Duration = 10m
|
|
17
|
+
connectionLifetime: Duration = 1h
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@effects(database, async, io)
|
|
21
|
+
type Connection {
|
|
22
|
+
@confidence(0.95)
|
|
23
|
+
@property("handles SQL injection safely")
|
|
24
|
+
@property("validates parameters")
|
|
25
|
+
func query<T>(self, sql: string, params: any[]) -> Promise<Result<T[], DbError>> {
|
|
26
|
+
// Execute parameterized query
|
|
27
|
+
// Prevents SQL injection via prepared statements
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@confidence(0.96)
|
|
31
|
+
@property("returns affected row count")
|
|
32
|
+
func execute(self, sql: string, params: any[]) -> Promise<Result<number, DbError>> {
|
|
33
|
+
// Execute non-query statement (INSERT, UPDATE, DELETE)
|
|
34
|
+
// Returns number of affected rows
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@confidence(0.99)
|
|
38
|
+
@effects(io)
|
|
39
|
+
func close(self) -> Promise<void> {
|
|
40
|
+
// Close database connection
|
|
41
|
+
// Returns connection to pool
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@effects(database, async, state)
|
|
46
|
+
type Transaction {
|
|
47
|
+
@confidence(0.94)
|
|
48
|
+
func query<T>(self, sql: string, params: any[]) -> Promise<Result<T[], DbError>>
|
|
49
|
+
|
|
50
|
+
@confidence(0.94)
|
|
51
|
+
func execute(self, sql: string, params: any[]) -> Promise<Result<number, DbError>>
|
|
52
|
+
|
|
53
|
+
@confidence(0.96)
|
|
54
|
+
@property("commits all changes or none")
|
|
55
|
+
func commit(self) -> Promise<Result<void, DbError>> {
|
|
56
|
+
// Commit transaction
|
|
57
|
+
// All-or-nothing guarantee
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@confidence(0.97)
|
|
61
|
+
@property("rolls back all changes")
|
|
62
|
+
func rollback(self) -> Promise<Result<void, DbError>> {
|
|
63
|
+
// Rollback transaction
|
|
64
|
+
// Cancels all pending changes
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@needs(config: DbConfig)
|
|
69
|
+
@effects(database, async, state, io)
|
|
70
|
+
type ConnectionPool {
|
|
71
|
+
config: DbConfig
|
|
72
|
+
|
|
73
|
+
@confidence(0.92)
|
|
74
|
+
@property("returns connection within timeout")
|
|
75
|
+
@property("recycles connections")
|
|
76
|
+
@property("limits max connections")
|
|
77
|
+
func acquire(self) -> Promise<Result<Connection, DbError>> {
|
|
78
|
+
@timeout(self.config.acquireTimeout)
|
|
79
|
+
conn = self.getOrCreateConnection()
|
|
80
|
+
|
|
81
|
+
if conn.isStale() {
|
|
82
|
+
conn.refresh()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return Ok(conn)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@confidence(0.97)
|
|
89
|
+
@property("connection returned to pool")
|
|
90
|
+
func release(self, conn: Connection) -> Promise<void> {
|
|
91
|
+
if self.pool.size() < self.config.maxConnections {
|
|
92
|
+
self.pool.add(conn)
|
|
93
|
+
} else {
|
|
94
|
+
conn.close()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@confidence(0.94)
|
|
99
|
+
@complete
|
|
100
|
+
@property("commits on success, rolls back on error")
|
|
101
|
+
@property("releases connection after transaction")
|
|
102
|
+
func transaction<T>(
|
|
103
|
+
self,
|
|
104
|
+
fn: (tx: Transaction) -> Promise<Result<T, Error>>
|
|
105
|
+
) -> Promise<Result<T, DbError>> {
|
|
106
|
+
conn = self.acquire() match {
|
|
107
|
+
Ok(c) -> c,
|
|
108
|
+
Err(e) -> return Err(e)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
tx = conn.beginTransaction()
|
|
112
|
+
|
|
113
|
+
result = fn(tx) match {
|
|
114
|
+
Ok(value) -> {
|
|
115
|
+
tx.commit() match {
|
|
116
|
+
Ok(_) -> Ok(value),
|
|
117
|
+
Err(e) -> {
|
|
118
|
+
tx.rollback()
|
|
119
|
+
Err(DbError.COMMIT_FAILED(e))
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
Err(error) -> {
|
|
124
|
+
tx.rollback()
|
|
125
|
+
Err(DbError.TRANSACTION_FAILED(error))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
self.release(conn)
|
|
130
|
+
|
|
131
|
+
return result
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@healthcheck(interval: 30s)
|
|
135
|
+
@confidence(0.96)
|
|
136
|
+
func checkHealth(self) -> HealthStatus {
|
|
137
|
+
try {
|
|
138
|
+
conn = self.acquire()
|
|
139
|
+
conn.query("SELECT 1", [])
|
|
140
|
+
self.release(conn)
|
|
141
|
+
return HealthStatus.OK
|
|
142
|
+
} catch {
|
|
143
|
+
return HealthStatus.FAILED
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@confidence(0.98)
|
|
148
|
+
func getTotalConnections(self) -> number {
|
|
149
|
+
return self.pool.size() + self.active.size()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@confidence(0.99)
|
|
153
|
+
func getIdleConnections(self) -> number {
|
|
154
|
+
return self.pool.size()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@confidence(0.99)
|
|
158
|
+
func getActiveConnections(self) -> number {
|
|
159
|
+
return self.active.size()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Factory function
|
|
164
|
+
@confidence(0.94)
|
|
165
|
+
@complete
|
|
166
|
+
@effects(database, async)
|
|
167
|
+
func createPool(config: DbConfig) -> Promise<Result<ConnectionPool, DbError>> {
|
|
168
|
+
pool = ConnectionPool { config: config }
|
|
169
|
+
|
|
170
|
+
// Initialize minimum connections
|
|
171
|
+
for i in 0..config.minConnections {
|
|
172
|
+
conn = pool.createConnection()
|
|
173
|
+
pool.pool.add(conn)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return Ok(pool)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Query builder (type-safe SQL)
|
|
180
|
+
@confidence(0.89)
|
|
181
|
+
@partial("Basic SELECT only, no JOIN/GROUP BY yet")
|
|
182
|
+
type QueryBuilder<T> {
|
|
183
|
+
table: string
|
|
184
|
+
conditions: Condition[]
|
|
185
|
+
limit: Option<number>
|
|
186
|
+
|
|
187
|
+
func where(self, field: string, op: string, value: any) -> QueryBuilder<T> {
|
|
188
|
+
self.conditions.push({ field, op, value })
|
|
189
|
+
return self
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
func limit(self, n: number) -> QueryBuilder<T> {
|
|
193
|
+
self.limit = Some(n)
|
|
194
|
+
return self
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@confidence(0.90)
|
|
198
|
+
@effects(database, async)
|
|
199
|
+
func execute(self, pool: ConnectionPool) -> Promise<Result<T[], DbError>> {
|
|
200
|
+
sql = self.buildSQL()
|
|
201
|
+
params = self.extractParams()
|
|
202
|
+
|
|
203
|
+
conn = pool.acquire()
|
|
204
|
+
result = conn.query<T>(sql, params)
|
|
205
|
+
pool.release(conn)
|
|
206
|
+
|
|
207
|
+
return result
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@confidence(0.95)
|
|
211
|
+
private func buildSQL(self) -> string {
|
|
212
|
+
sql = "SELECT * FROM " + self.table
|
|
213
|
+
|
|
214
|
+
if self.conditions.length > 0 {
|
|
215
|
+
sql = sql + " WHERE " + self.conditions.map(c =>
|
|
216
|
+
c.field + " " + c.op + " ?"
|
|
217
|
+
).join(" AND ")
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if self.limit {
|
|
221
|
+
sql = sql + " LIMIT " + self.limit
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return sql
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@confidence(0.92)
|
|
229
|
+
func select<T>(table: string) -> QueryBuilder<T> {
|
|
230
|
+
return QueryBuilder<T> {
|
|
231
|
+
table: table,
|
|
232
|
+
conditions: [],
|
|
233
|
+
limit: None
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// File I/O Module
|
|
2
|
+
// @agentic/fs - Buffered file operations with error recovery
|
|
3
|
+
|
|
4
|
+
@module("file")
|
|
5
|
+
@confidence(0.94)
|
|
6
|
+
|
|
7
|
+
type FileMode = "r" | "w" | "a" | "r+" | "w+" | "a+"
|
|
8
|
+
|
|
9
|
+
type WriteOptions {
|
|
10
|
+
overwrite: boolean = false
|
|
11
|
+
createDirectories: boolean = false
|
|
12
|
+
encoding: string = "utf-8"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@effects(file_system, async, io)
|
|
16
|
+
@confidence(0.96)
|
|
17
|
+
@complete
|
|
18
|
+
@property("handles missing files")
|
|
19
|
+
@property("handles permission errors")
|
|
20
|
+
@property("respects encoding")
|
|
21
|
+
@needs(fs: FileSystem)
|
|
22
|
+
func readFile(path: string, encoding: string = "utf-8") -> Promise<Result<string, FileError>> {
|
|
23
|
+
if !fs.exists(path) {
|
|
24
|
+
return Err(FileError.NOT_FOUND(path))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@retry(maxAttempts: 2, delay: 100ms)
|
|
28
|
+
content = fs.read(path, encoding) or error {
|
|
29
|
+
@context {
|
|
30
|
+
what_failed: "File read",
|
|
31
|
+
path: path,
|
|
32
|
+
encoding: encoding,
|
|
33
|
+
suggestions: [
|
|
34
|
+
"Check file permissions",
|
|
35
|
+
"Verify path is correct",
|
|
36
|
+
"Try different encoding (utf-8, ascii, utf-16)"
|
|
37
|
+
],
|
|
38
|
+
recovery: {
|
|
39
|
+
action: "use_default",
|
|
40
|
+
command: "echo '' > ${path}"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return Err(FileError.READ_ERROR(error))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Ok(content)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@effects(file_system, async, io)
|
|
50
|
+
@confidence(0.95)
|
|
51
|
+
@complete
|
|
52
|
+
@property("creates parent directories if requested")
|
|
53
|
+
@property("respects overwrite option")
|
|
54
|
+
@property("handles write errors gracefully")
|
|
55
|
+
@needs(fs: FileSystem)
|
|
56
|
+
func writeFile(
|
|
57
|
+
path: string,
|
|
58
|
+
content: string,
|
|
59
|
+
options: WriteOptions = WriteOptions{}
|
|
60
|
+
) -> Promise<Result<void, FileError>> {
|
|
61
|
+
if fs.exists(path) and !options.overwrite {
|
|
62
|
+
return Err(FileError.ALREADY_EXISTS(path))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if options.createDirectories {
|
|
66
|
+
parentDir = path.dirname()
|
|
67
|
+
if !fs.exists(parentDir) {
|
|
68
|
+
fs.createDir(parentDir, recursive: true)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@retry(maxAttempts: 2, delay: 100ms)
|
|
73
|
+
fs.write(path, content, options.encoding) or error {
|
|
74
|
+
@context {
|
|
75
|
+
what_failed: "File write",
|
|
76
|
+
path: path,
|
|
77
|
+
size: content.length,
|
|
78
|
+
suggestions: [
|
|
79
|
+
"Check disk space",
|
|
80
|
+
"Verify permissions",
|
|
81
|
+
"Check if path is valid"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
return Err(FileError.WRITE_ERROR(error))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return Ok(void)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Buffered reader for large files
|
|
91
|
+
@effects(file_system, async, io, state)
|
|
92
|
+
type BufferedReader {
|
|
93
|
+
bufferSize: number = 64 * 1024 // 64KB default
|
|
94
|
+
|
|
95
|
+
@confidence(0.95)
|
|
96
|
+
@property("returns None on EOF")
|
|
97
|
+
func readLine(self) -> Promise<Result<Option<string>, IoError>> {
|
|
98
|
+
// Read next line from buffer
|
|
99
|
+
// Returns None when EOF reached
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@confidence(0.96)
|
|
103
|
+
func readChunk(self, size: number) -> Promise<Result<Buffer, IoError>> {
|
|
104
|
+
// Read exactly size bytes
|
|
105
|
+
// Returns smaller chunk at EOF
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@confidence(0.94)
|
|
109
|
+
func readAll(self) -> Promise<Result<string, IoError>> {
|
|
110
|
+
lines: string[] = []
|
|
111
|
+
|
|
112
|
+
loop {
|
|
113
|
+
line = self.readLine() match {
|
|
114
|
+
Ok(Some(l)) -> l,
|
|
115
|
+
Ok(None) -> break,
|
|
116
|
+
Err(e) -> return Err(e)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
lines.push(line)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return Ok(lines.join("\n"))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@confidence(0.98)
|
|
126
|
+
func close(self) -> Promise<void> {
|
|
127
|
+
// Close file handle and flush buffer
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Stream processing for huge files
|
|
132
|
+
@effects(file_system, async, io)
|
|
133
|
+
@confidence(0.92)
|
|
134
|
+
@partial("Memory-efficient but no backpressure yet")
|
|
135
|
+
func stream(path: string) -> AsyncIterator<string> {
|
|
136
|
+
reader = BufferedReader { bufferSize: 64 * 1024 }
|
|
137
|
+
|
|
138
|
+
return async iterator {
|
|
139
|
+
loop {
|
|
140
|
+
line = reader.readLine() match {
|
|
141
|
+
Ok(Some(l)) -> yield l,
|
|
142
|
+
Ok(None) -> return,
|
|
143
|
+
Err(e) -> throw e
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Directory operations
|
|
150
|
+
@effects(file_system, io)
|
|
151
|
+
@confidence(0.96)
|
|
152
|
+
@complete
|
|
153
|
+
@property("returns empty array for empty directory")
|
|
154
|
+
@property("handles missing directory")
|
|
155
|
+
func listDir(path: string) -> Promise<Result<string[], FileError>> {
|
|
156
|
+
if !fs.exists(path) {
|
|
157
|
+
return Err(FileError.NOT_FOUND(path))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if !fs.isDirectory(path) {
|
|
161
|
+
return Err(FileError.NOT_A_DIRECTORY(path))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
entries = fs.readDir(path) or error {
|
|
165
|
+
return Err(FileError.READ_ERROR(error))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return Ok(entries)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@effects(file_system, io)
|
|
172
|
+
@confidence(0.97)
|
|
173
|
+
@complete
|
|
174
|
+
@property("creates directory if not exists")
|
|
175
|
+
@property("handles recursive creation")
|
|
176
|
+
func createDir(path: string, recursive: boolean = false) -> Promise<Result<void, FileError>> {
|
|
177
|
+
if fs.exists(path) {
|
|
178
|
+
return Ok(void) // Already exists
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
options = { recursive: recursive }
|
|
182
|
+
|
|
183
|
+
fs.mkdir(path, options) or error {
|
|
184
|
+
@context {
|
|
185
|
+
what_failed: "Directory creation",
|
|
186
|
+
path: path,
|
|
187
|
+
recursive: recursive,
|
|
188
|
+
suggestions: ["Check parent directory exists", "Verify permissions"]
|
|
189
|
+
}
|
|
190
|
+
return Err(FileError.CREATE_ERROR(error))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return Ok(void)
|
|
194
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// HTTP Client Module
|
|
2
|
+
// @agentic/http - Production-grade HTTP client with connection pooling
|
|
3
|
+
|
|
4
|
+
@module("http")
|
|
5
|
+
@confidence(0.95)
|
|
6
|
+
|
|
7
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD"
|
|
8
|
+
|
|
9
|
+
type HttpHeaders = Map<string, string>
|
|
10
|
+
|
|
11
|
+
type HttpRequest {
|
|
12
|
+
method: HttpMethod
|
|
13
|
+
url: string
|
|
14
|
+
headers: HttpHeaders
|
|
15
|
+
body: Option<string>
|
|
16
|
+
timeout: Option<Duration>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type HttpResponse {
|
|
20
|
+
status: number
|
|
21
|
+
headers: HttpHeaders
|
|
22
|
+
body: string
|
|
23
|
+
|
|
24
|
+
@confidence(0.98)
|
|
25
|
+
func json<T>(self) -> Result<T, ParseError> {
|
|
26
|
+
return JSON.parse(self.body)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@confidence(0.99)
|
|
30
|
+
func text(self) -> string {
|
|
31
|
+
return self.body
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type HttpClientConfig {
|
|
36
|
+
baseUrl: Option<string>
|
|
37
|
+
defaultHeaders: HttpHeaders
|
|
38
|
+
timeout: Duration = 30s
|
|
39
|
+
maxConnections: number = 100
|
|
40
|
+
keepAlive: boolean = true
|
|
41
|
+
retryPolicy: Option<RetryPolicy>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@needs(config: HttpClientConfig)
|
|
45
|
+
@effects(network, async, io)
|
|
46
|
+
type HttpClient {
|
|
47
|
+
config: HttpClientConfig
|
|
48
|
+
|
|
49
|
+
@confidence(0.92)
|
|
50
|
+
@property("handles network errors gracefully")
|
|
51
|
+
@property("respects timeout")
|
|
52
|
+
@property("retries on transient failures")
|
|
53
|
+
func get(self, url: string) -> Promise<Result<HttpResponse, HttpError>> {
|
|
54
|
+
request = HttpRequest {
|
|
55
|
+
method: "GET",
|
|
56
|
+
url: self.resolveUrl(url),
|
|
57
|
+
headers: self.config.defaultHeaders,
|
|
58
|
+
body: None,
|
|
59
|
+
timeout: Some(self.config.timeout)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return self.request(request)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@confidence(0.92)
|
|
66
|
+
@property("validates request body")
|
|
67
|
+
@property("sets content-type header")
|
|
68
|
+
func post(self, url: string, body: string) -> Promise<Result<HttpResponse, HttpError>> {
|
|
69
|
+
headers = self.config.defaultHeaders.clone()
|
|
70
|
+
headers.set("Content-Type", "application/json")
|
|
71
|
+
|
|
72
|
+
request = HttpRequest {
|
|
73
|
+
method: "POST",
|
|
74
|
+
url: self.resolveUrl(url),
|
|
75
|
+
headers: headers,
|
|
76
|
+
body: Some(body),
|
|
77
|
+
timeout: Some(self.config.timeout)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return self.request(request)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@confidence(0.90)
|
|
84
|
+
@complete
|
|
85
|
+
@needs(network: NetworkAccess, logger: Logger)
|
|
86
|
+
@effects(network, async, io, exception)
|
|
87
|
+
func request(self, req: HttpRequest) -> Promise<Result<HttpResponse, HttpError>> {
|
|
88
|
+
logger.info("HTTP ${req.method} ${req.url}")
|
|
89
|
+
|
|
90
|
+
@retry(maxAttempts: 3, backoff: "exponential")
|
|
91
|
+
@circuit_breaker(threshold: 5, timeout: 60s)
|
|
92
|
+
response = network.fetch(req.url, {
|
|
93
|
+
method: req.method,
|
|
94
|
+
headers: req.headers,
|
|
95
|
+
body: req.body,
|
|
96
|
+
timeout: req.timeout
|
|
97
|
+
}) match {
|
|
98
|
+
Ok(resp) -> {
|
|
99
|
+
logger.info("HTTP ${req.method} ${req.url} -> ${resp.status}")
|
|
100
|
+
return Ok(resp)
|
|
101
|
+
},
|
|
102
|
+
Err(error) -> {
|
|
103
|
+
@context {
|
|
104
|
+
what_failed: "HTTP request",
|
|
105
|
+
request: { method: req.method, url: req.url },
|
|
106
|
+
suggestions: [
|
|
107
|
+
"Check network connectivity",
|
|
108
|
+
"Verify URL is correct",
|
|
109
|
+
"Check if server is running"
|
|
110
|
+
],
|
|
111
|
+
recovery: {
|
|
112
|
+
action: "retry",
|
|
113
|
+
command: "http.request(req)"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
logger.error("HTTP ${req.method} ${req.url} failed", error)
|
|
117
|
+
return Err(HttpError.NETWORK_ERROR(error))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@confidence(0.99)
|
|
123
|
+
private func resolveUrl(self, path: string) -> string {
|
|
124
|
+
if path.startsWith("http://") or path.startsWith("https://") {
|
|
125
|
+
return path
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if self.config.baseUrl {
|
|
129
|
+
return self.config.baseUrl + path
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return path
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Factory function
|
|
137
|
+
@confidence(0.98)
|
|
138
|
+
@complete
|
|
139
|
+
func createClient(config: HttpClientConfig) -> HttpClient {
|
|
140
|
+
return HttpClient { config: config }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@confidence(0.99)
|
|
144
|
+
func defaultClient() -> HttpClient {
|
|
145
|
+
return createClient(HttpClientConfig {
|
|
146
|
+
baseUrl: None,
|
|
147
|
+
defaultHeaders: Map.new(),
|
|
148
|
+
timeout: Duration.seconds(30),
|
|
149
|
+
maxConnections: 100,
|
|
150
|
+
keepAlive: true,
|
|
151
|
+
retryPolicy: None
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Convenience helpers
|
|
156
|
+
@confidence(0.95)
|
|
157
|
+
@effects(network, async)
|
|
158
|
+
func get(url: string) -> Promise<Result<HttpResponse, HttpError>> {
|
|
159
|
+
client = defaultClient()
|
|
160
|
+
return client.get(url)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@confidence(0.95)
|
|
164
|
+
@effects(network, async)
|
|
165
|
+
func post(url: string, body: string) -> Promise<Result<HttpResponse, HttpError>> {
|
|
166
|
+
client = defaultClient()
|
|
167
|
+
return client.post(url, body)
|
|
168
|
+
}
|