From 4ca01d199d23649507ddf2e59af2bccb69b04922 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Tue, 28 Oct 2025 23:50:57 -0700 Subject: [PATCH] feat: migrate to Letta TS SDK v1 (alpha) (#11) --- bun.lock | 228 ++++++++++----------------------- package.json | 2 +- src/agent/check-approval.ts | 39 ++++-- src/agent/client.ts | 10 +- src/agent/create.ts | 22 ++-- src/agent/memory.ts | 12 +- src/agent/message.ts | 15 ++- src/agent/modify.ts | 18 +-- src/cli/App.tsx | 122 +++++++++++++----- src/cli/commands/registry.ts | 7 + src/cli/helpers/accumulator.ts | 64 +++++---- src/cli/helpers/backfill.ts | 33 +++-- src/cli/helpers/stream.ts | 65 ++++++---- src/headless.ts | 24 ++-- src/index.ts | 4 +- src/tests/message.smoke.ts | 34 ++--- src/tools/manager.ts | 10 +- 17 files changed, 377 insertions(+), 332 deletions(-) diff --git a/bun.lock b/bun.lock index d2cb45c..8a8034a 100644 --- a/bun.lock +++ b/bun.lock @@ -2,12 +2,12 @@ "lockfileVersion": 1, "workspaces": { "": { - "name": "letta-code", + "name": "@letta-ai/letta-code", "dependencies": { + "@letta-ai/letta-client": "1.0.0-alpha.10", "ink-link": "^5.0.0", }, "devDependencies": { - "@letta-ai/letta-client": "1.0.0-alpha.2", "@types/bun": "latest", "@types/diff": "^8.0.0", "diff": "^8.0.2", @@ -32,133 +32,81 @@ "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], - "@letta-ai/letta-client": ["@letta-ai/letta-client@1.0.0-alpha.2", "", { "dependencies": { "form-data": "^4.0.0", "form-data-encoder": "^4.0.2", "formdata-node": "^6.0.3", "node-fetch": "^2.7.0", "qs": "^6.13.1", "readable-stream": "^4.5.2", "url-join": "4.0.1" } }, ""], + "@letta-ai/letta-client": ["@letta-ai/letta-client@1.0.0-alpha.10", "", {}, "sha512-UV9b5rbkEPX2+7Dp0gD3Hk8KHbubz6K3GoAF1u813UZCguJcQO2azXNmjyvinQSPhKxlB/WYT/79toB4ElljSw=="], - "@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="], + "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], - "@types/diff": ["@types/diff@8.0.0", "", { "dependencies": { "diff": "*" } }, ""], + "@types/diff": ["@types/diff@8.0.0", "", { "dependencies": { "diff": "*" } }, "sha512-o7jqJM04gfaYrdCecCVMbZhNdG6T1MHg/oQoRFdERLV+4d+V7FijhiEAbFu0Usww84Yijk9yH58U4Jk4HbtzZw=="], - "@types/node": ["@types/node@24.7.2", "", { "dependencies": { "undici-types": "~7.14.0" } }, ""], + "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], - "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, ""], + "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], "@vscode/ripgrep": ["@vscode/ripgrep@1.17.0", "", { "dependencies": { "https-proxy-agent": "^7.0.2", "proxy-from-env": "^1.1.0", "yauzl": "^2.9.2" } }, "sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g=="], - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, ""], - "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "ansi-escapes": ["ansi-escapes@7.1.1", "", { "dependencies": { "environment": "^1.0.0" } }, ""], + "ansi-escapes": ["ansi-escapes@7.1.1", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, ""], + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "ansi-styles": ["ansi-styles@6.2.3", "", {}, ""], + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "asynckit": ["asynckit@0.4.0", "", {}, ""], + "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], - "auto-bind": ["auto-bind@5.0.1", "", {}, ""], - - "base64-js": ["base64-js@1.5.1", "", {}, ""], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, ""], - - "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, ""], + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], - "bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, ""], + "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, ""], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, ""], + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], - "chalk": ["chalk@5.6.2", "", {}, ""], - - "cli-boxes": ["cli-boxes@3.0.0", "", {}, ""], - - "cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, ""], + "cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], - "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, ""], + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], - "code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, ""], + "code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="], - "colorette": ["colorette@2.0.20", "", {}, ""], + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, ""], + "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="], - "commander": ["commander@14.0.1", "", {}, ""], + "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], - "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, ""], - - "csstype": ["csstype@3.1.3", "", {}, ""], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "delayed-stream": ["delayed-stream@1.0.0", "", {}, ""], + "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], - "diff": ["diff@8.0.2", "", {}, ""], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, ""], + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], - "emoji-regex": ["emoji-regex@10.5.0", "", {}, ""], + "es-toolkit": ["es-toolkit@1.41.0", "", {}, "sha512-bDd3oRmbVgqZCJS6WmeQieOrzpl3URcWBUVDXxOELlUW2FuW+0glPOz1n0KnRie+PdyvUZcXz2sOn00c6pPRIA=="], - "environment": ["environment@1.1.0", "", {}, ""], + "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, ""], - - "es-errors": ["es-errors@1.3.0", "", {}, ""], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, ""], - - "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, ""], - - "es-toolkit": ["es-toolkit@1.40.0", "", {}, ""], - - "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, ""], - - "event-target-shim": ["event-target-shim@5.0.1", "", {}, ""], - - "eventemitter3": ["eventemitter3@5.0.1", "", {}, ""], - - "events": ["events@3.3.0", "", {}, ""], + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, ""], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, ""], - - "form-data-encoder": ["form-data-encoder@4.1.0", "", {}, ""], - - "formdata-node": ["formdata-node@6.0.3", "", {}, ""], - - "function-bind": ["function-bind@1.1.2", "", {}, ""], - - "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, ""], - - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, ""], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, ""], - - "gopd": ["gopd@1.2.0", "", {}, ""], + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], "has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="], - "has-symbols": ["has-symbols@1.1.0", "", {}, ""], - - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, ""], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, ""], - "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - "husky": ["husky@9.1.7", "", { "bin": "bin.js" }, ""], + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], - "ieee754": ["ieee754@1.2.1", "", {}, ""], - - "indent-string": ["indent-string@5.0.0", "", {}, ""], + "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], "ink": ["ink@5.2.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.22.0", "indent-string": "^5.0.0", "is-in-ci": "^1.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.29.0", "scheduler": "^0.23.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0", "react-devtools-core": "^4.19.1" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg=="], @@ -168,95 +116,67 @@ "ink-text-input": ["ink-text-input@5.0.1", "", { "dependencies": { "chalk": "^5.2.0", "type-fest": "^3.6.1" }, "peerDependencies": { "ink": "^4.0.0", "react": "^18.0.0" } }, "sha512-crnsYJalG4EhneOFnr/q+Kzw1RgmXI2KsBaLFE6mpiIKxAtJLUnvygOF2IUKO8z4nwkSkveGRBMd81RoYdRSag=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, ""], + "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], "is-in-ci": ["is-in-ci@1.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="], - "is-number": ["is-number@7.0.0", "", {}, ""], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "lint-staged": ["lint-staged@16.2.4", "", { "dependencies": { "commander": "^14.0.1", "listr2": "^9.0.4", "micromatch": "^4.0.8", "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": "bin/lint-staged.js" }, ""], + "lint-staged": ["lint-staged@16.2.4", "", { "dependencies": { "commander": "^14.0.1", "listr2": "^9.0.4", "micromatch": "^4.0.8", "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-Pkyr/wd90oAyXk98i/2KwfkIhoYQUMtss769FIT9hFM5ogYZwrk+GRE46yKXSg2ZGhcJ1p38Gf5gmI5Ohjg2yg=="], - "listr2": ["listr2@9.0.4", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, ""], + "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], - "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, ""], + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, ""], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, ""], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "mime-db": ["mime-db@1.52.0", "", {}, ""], - - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, ""], - - "mimic-fn": ["mimic-fn@2.1.0", "", {}, ""], - - "mimic-function": ["mimic-function@5.0.1", "", {}, ""], + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], "minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nano-spawn": ["nano-spawn@2.0.0", "", {}, ""], + "nano-spawn": ["nano-spawn@2.0.0", "", {}, "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw=="], - "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, ""], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - "object-inspect": ["object-inspect@1.13.4", "", {}, ""], - - "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, ""], - - "patch-console": ["patch-console@2.0.0", "", {}, ""], + "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], - "picomatch": ["picomatch@2.3.1", "", {}, ""], + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "pidtree": ["pidtree@0.6.0", "", { "bin": "bin/pidtree.js" }, ""], - - "process": ["process@0.11.10", "", {}, ""], + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], - "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, ""], - "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], "react-reconciler": ["react-reconciler@0.29.2", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="], - "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, ""], + "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], - "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, ""], - - "rfdc": ["rfdc@1.4.1", "", {}, ""], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, ""], + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, ""], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, ""], + "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, ""], + "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, ""], + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], - "signal-exit": ["signal-exit@3.0.7", "", {}, ""], + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, ""], - - "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, ""], - - "string-argv": ["string-argv@0.3.2", "", {}, ""], - - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, ""], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, ""], - - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, ""], + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], @@ -264,50 +184,42 @@ "terminal-link": ["terminal-link@5.0.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "supports-hyperlinks": "^4.1.0" } }, "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, ""], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "tr46": ["tr46@0.0.3", "", {}, ""], + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "type-fest": ["type-fest@4.41.0", "", {}, ""], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, ""], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "undici-types": ["undici-types@7.14.0", "", {}, ""], + "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], - "url-join": ["url-join@4.0.1", "", {}, ""], + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], - "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, ""], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, ""], - - "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, ""], - - "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, ""], - - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, ""], - - "yaml": ["yaml@2.8.1", "", { "bin": "bin.mjs" }, ""], + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], - "yoga-layout": ["yoga-layout@3.2.1", "", {}, ""], + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, ""], + "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], "ink-text-input/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="], - "listr2/cli-truncate": ["cli-truncate@5.1.0", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, ""], + "listr2/cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="], - "log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, ""], + "log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], - "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, ""], + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], - "listr2/cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, ""], + "listr2/cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], - "log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, ""], + "log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], - "log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, ""], + "log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], - "log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, ""], + "log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], } } diff --git a/package.json b/package.json index 5a59325..be4cb33 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "access": "public" }, "dependencies": { + "@letta-ai/letta-client": "1.0.0-alpha.10", "ink-link": "^5.0.0" }, "optionalDependencies": { @@ -33,7 +34,6 @@ "typescript": "^5.0.0", "husky": "9.1.7", "lint-staged": "16.2.4", - "@letta-ai/letta-client": "1.0.0-alpha.2", "diff": "^8.0.2", "ink": "^5.0.0", "ink-spinner": "^5.0.0", diff --git a/src/agent/check-approval.ts b/src/agent/check-approval.ts index 3d27249..ffde657 100644 --- a/src/agent/check-approval.ts +++ b/src/agent/check-approval.ts @@ -1,7 +1,8 @@ // src/agent/check-approval.ts // Check for pending approvals and retrieve recent message history when resuming an agent -import type { Letta, LettaClient } from "@letta-ai/letta-client"; +import type Letta from "@letta-ai/letta-client"; +import type { LettaMessageUnion } from "@letta-ai/letta-client/resources/agents/messages"; import type { ApprovalRequest } from "../cli/helpers/stream"; // Number of recent messages to backfill when resuming a session @@ -9,7 +10,7 @@ const MESSAGE_HISTORY_LIMIT = 15; export interface ResumeData { pendingApproval: ApprovalRequest | null; - messageHistory: Letta.LettaMessageUnion[]; + messageHistory: LettaMessageUnion[]; } /** @@ -21,11 +22,12 @@ export interface ResumeData { * @returns Pending approval (if any) and recent message history */ export async function getResumeData( - client: LettaClient, + client: Letta, agentId: string, ): Promise { try { - const messages = await client.agents.messages.list(agentId); + const messagesPage = await client.agents.messages.list(agentId); + const messages = messagesPage.items; if (!messages || messages.length === 0) { return { pendingApproval: null, messageHistory: [] }; } @@ -33,14 +35,25 @@ export async function getResumeData( // Check for pending approval (last message) let pendingApproval: ApprovalRequest | null = null; const lastMessage = messages[messages.length - 1]; - if (lastMessage?.messageType === "approval_request_message") { - const approvalMessage = lastMessage as Letta.ApprovalRequestMessage; - const toolCall = approvalMessage.toolCall; - pendingApproval = { - toolCallId: toolCall.toolCallId || "", - toolName: toolCall.name || "", - toolArgs: toolCall.arguments || "", - }; + if (lastMessage?.message_type === "approval_request_message") { + // Use tool_calls array (new) or fallback to tool_call (deprecated) + const toolCalls = Array.isArray(lastMessage.tool_calls) + ? lastMessage.tool_calls + : lastMessage.tool_call + ? [lastMessage.tool_call] + : []; + + if (toolCalls.length > 0) { + const toolCall = toolCalls[0]; + // Ensure all required fields are present (type guard for ToolCall vs ToolCallDelta) + if (toolCall?.tool_call_id && toolCall.name && toolCall.arguments) { + pendingApproval = { + toolCallId: toolCall.tool_call_id, + toolName: toolCall.name, + toolArgs: toolCall.arguments, + }; + } + } } // Get last N messages for backfill @@ -48,7 +61,7 @@ export async function getResumeData( let messageHistory = messages.slice(-historyCount); // Skip if starts with orphaned tool_return (incomplete turn) - if (messageHistory[0]?.messageType === "tool_return_message") { + if (messageHistory[0]?.message_type === "tool_return_message") { messageHistory = messageHistory.slice(1); } diff --git a/src/agent/client.ts b/src/agent/client.ts index 7fe8ecb..c1094bb 100644 --- a/src/agent/client.ts +++ b/src/agent/client.ts @@ -1,11 +1,11 @@ -import { LettaClient } from "@letta-ai/letta-client"; +import Letta from "@letta-ai/letta-client"; import { loadSettings, updateSettings } from "../settings"; export async function getClient() { const settings = await loadSettings(); - const token = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY; - if (!token) { + const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY; + if (!apiKey) { console.error("Missing LETTA_API_KEY"); console.error( "Set it via environment variable or add it to ~/.letta/settings.json:", @@ -14,7 +14,7 @@ export async function getClient() { process.exit(1); } - const baseUrl = + const baseURL = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || "https://api.letta.com"; @@ -37,5 +37,5 @@ export async function getClient() { await updateSettings({ env: updatedEnv }); } - return new LettaClient({ token, baseUrl }); + return new Letta({ apiKey, baseURL }); } diff --git a/src/agent/create.ts b/src/agent/create.ts index 3dd9147..bc98df6 100644 --- a/src/agent/create.ts +++ b/src/agent/create.ts @@ -2,7 +2,11 @@ * Utilities for creating an agent on the Letta API backend **/ -import { Letta } from "@letta-ai/letta-client"; +import type { + AgentType, + Block, + CreateBlock, +} from "@letta-ai/letta-client/resources/agents/agents"; import { loadProjectSettings, updateProjectSettings, @@ -39,7 +43,7 @@ export async function createAgent( const localSharedBlockIds = projectSettings.localSharedBlockIds; // Retrieve existing blocks (both global and local) and match them with defaults - const existingBlocks = new Map(); + const existingBlocks = new Map(); // Load global blocks (persona, human) for (const [label, blockId] of Object.entries(globalSharedBlockIds)) { @@ -69,7 +73,7 @@ export async function createAgent( // Separate blocks into existing (reuse) and new (create) const blockIds: string[] = []; - const blocksToCreate: Array<{ block: Letta.CreateBlock; label: string }> = []; + const blocksToCreate: Array<{ block: CreateBlock; label: string }> = []; for (const defaultBlock of defaultMemoryBlocks) { const existingBlock = existingBlocks.get(defaultBlock.label); @@ -131,17 +135,17 @@ export async function createAgent( // Create agent with all block IDs (existing + newly created) const agent = await client.agents.create({ - agentType: Letta.AgentType.LettaV1Agent, + agent_type: "letta_v1_agent" as AgentType, system: SYSTEM_PROMPT, name, model, - contextWindowLimit: 200_000, + context_window_limit: 200_000, tools: toolNames, - blockIds, + block_ids: blockIds, // should be default off, but just in case - includeBaseTools: false, - includeBaseToolRules: false, - initialMessageSequence: [], + include_base_tools: false, + include_base_tool_rules: false, + initial_message_sequence: [], }); return agent; // { id, ... } } diff --git a/src/agent/memory.ts b/src/agent/memory.ts index b01e981..7b67fe4 100644 --- a/src/agent/memory.ts +++ b/src/agent/memory.ts @@ -3,7 +3,7 @@ * Loads memory blocks from .mdx files in src/agent/prompts */ -import type { Letta } from "@letta-ai/letta-client"; +import type { CreateBlock } from "@letta-ai/letta-client/resources/agents/agents"; import { MEMORY_PROMPTS } from "./promptAssets"; /** @@ -40,8 +40,8 @@ function parseMdxFrontmatter(content: string): { /** * Load memory blocks from .mdx files in src/agent/prompts */ -async function loadMemoryBlocksFromMdx(): Promise { - const memoryBlocks: Letta.CreateBlock[] = []; +async function loadMemoryBlocksFromMdx(): Promise { + const memoryBlocks: CreateBlock[] = []; const mdxFiles = ["persona.mdx", "human.mdx", "project.mdx"]; // const mdxFiles = ["persona.mdx", "human.mdx", "style.mdx"]; @@ -56,7 +56,7 @@ async function loadMemoryBlocksFromMdx(): Promise { } const { frontmatter, body } = parseMdxFrontmatter(content); - const block: Letta.CreateBlock = { + const block: CreateBlock = { label: frontmatter.label || filename.replace(".mdx", ""), value: body, }; @@ -75,12 +75,12 @@ async function loadMemoryBlocksFromMdx(): Promise { } // Cache for loaded memory blocks -let cachedMemoryBlocks: Letta.CreateBlock[] | null = null; +let cachedMemoryBlocks: CreateBlock[] | null = null; /** * Get default starter memory blocks for new agents */ -export async function getDefaultMemoryBlocks(): Promise { +export async function getDefaultMemoryBlocks(): Promise { if (!cachedMemoryBlocks) { cachedMemoryBlocks = await loadMemoryBlocksFromMdx(); } diff --git a/src/agent/message.ts b/src/agent/message.ts index 2fdbbb5..47f5d7e 100644 --- a/src/agent/message.ts +++ b/src/agent/message.ts @@ -2,22 +2,27 @@ * Utilities for sending messages to an agent **/ -import type { Letta } from "@letta-ai/letta-client"; +import type { Stream } from "@letta-ai/letta-client/core/streaming"; +import type { MessageCreate } from "@letta-ai/letta-client/resources/agents/agents"; +import type { + ApprovalCreate, + LettaStreamingResponse, +} from "@letta-ai/letta-client/resources/agents/messages"; import { getClient } from "./client"; export async function sendMessageStream( agentId: string, - messages: Array, + messages: Array, opts: { streamTokens?: boolean; background?: boolean; // add more later: includePings, request timeouts, etc. } = { streamTokens: true, background: true }, -): Promise> { +): Promise> { const client = await getClient(); - return client.agents.messages.createStream(agentId, { + return client.agents.messages.stream(agentId, { messages: messages, - streamTokens: opts.streamTokens ?? true, + stream_tokens: opts.streamTokens ?? true, background: opts.background ?? true, }); } diff --git a/src/agent/modify.ts b/src/agent/modify.ts index fec375b..5a150e9 100644 --- a/src/agent/modify.ts +++ b/src/agent/modify.ts @@ -1,7 +1,7 @@ // src/agent/modify.ts // Utilities for modifying agent configuration -import type { Letta } from "@letta-ai/letta-client"; +import type { LlmConfig } from "@letta-ai/letta-client/resources/models/models"; import { getClient } from "./client"; /** @@ -19,27 +19,27 @@ export async function updateAgentLLMConfig( agentId: string, modelHandle: string, updateArgs?: Record, -): Promise { +): Promise { const client = await getClient(); // Step 1: Update model (top-level field) - await client.agents.modify(agentId, { model: modelHandle }); + await client.agents.update(agentId, { model: modelHandle }); - // Step 2: Get updated agent to retrieve current llmConfig + // Step 2: Get updated agent to retrieve current llm_config const agent = await client.agents.retrieve(agentId); - let finalConfig = agent.llmConfig; + let finalConfig = agent.llm_config; - // Step 3: If we have updateArgs, merge them into llmConfig and patch again + // Step 3: If we have updateArgs, merge them into llm_config and patch again if (updateArgs && Object.keys(updateArgs).length > 0) { const updatedLlmConfig = { ...finalConfig, ...updateArgs, - } as Letta.LlmConfig; - await client.agents.modify(agentId, { llmConfig: updatedLlmConfig }); + } as LlmConfig; + await client.agents.update(agentId, { llm_config: updatedLlmConfig }); // Retrieve final state const finalAgent = await client.agents.retrieve(agentId); - finalConfig = finalAgent.llmConfig; + finalConfig = finalAgent.llm_config; } return finalConfig; diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 5486500..84b1d63 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -1,6 +1,11 @@ // src/cli/App.tsx -import { Letta } from "@letta-ai/letta-client"; +import type { MessageCreate } from "@letta-ai/letta-client/resources/agents/agents"; +import type { + ApprovalCreate, + LettaMessageUnion, +} from "@letta-ai/letta-client/resources/agents/messages"; +import type { LlmConfig } from "@letta-ai/letta-client/resources/models/models"; import { Box, Static } from "ink"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { getResumeData } from "../agent/check-approval"; @@ -105,7 +110,7 @@ export default function App({ | "ready"; continueSession?: boolean; startupApproval?: ApprovalRequest | null; - messageHistory?: Letta.LettaMessageUnion[]; + messageHistory?: LettaMessageUnion[]; tokenStreaming?: boolean; }) { // Whether a stream is in flight (disables input) @@ -132,7 +137,7 @@ export default function App({ // Model selector state const [modelSelectorOpen, setModelSelectorOpen] = useState(false); - const [llmConfig, setLlmConfig] = useState(null); + const [llmConfig, setLlmConfig] = useState(null); // Token streaming preference (can be toggled at runtime) const [tokenStreamingEnabled, setTokenStreamingEnabled] = @@ -345,9 +350,9 @@ export default function App({ const { getClient } = await import("../agent/client"); const client = await getClient(); const agent = await client.agents.retrieve(agentId); - setLlmConfig(agent.llmConfig); + setLlmConfig(agent.llm_config); } catch (error) { - console.error("Error fetching llmConfig:", error); + console.error("Error fetching llm_config:", error); } }; fetchConfig(); @@ -372,7 +377,7 @@ export default function App({ // Core streaming function - iterative loop that processes conversation turns const processConversation = useCallback( async ( - initialInput: Array, + initialInput: Array, ): Promise => { let currentInput = initialInput; @@ -398,7 +403,7 @@ export default function App({ refreshDerived(); // Case 1: Turn ended normally - if (stopReason === Letta.StopReasonType.EndTurn) { + if (stopReason === "end_turn") { setStreaming(false); return; } @@ -411,7 +416,7 @@ export default function App({ } // Case 2: Requires approval - if (stopReason === Letta.StopReasonType.RequiresApproval) { + if (stopReason === "requires_approval") { if (!approval) { appendError( `Unexpected null approval with stop reason: ${stopReason}`, @@ -453,7 +458,7 @@ export default function App({ await processConversation([ { type: "approval", - approvalRequestId: toolCallId, + approval_request_id: toolCallId, approve: false, reason: denyReason, }, @@ -479,11 +484,11 @@ export default function App({ // Update buffers with tool return onChunk(buffersRef.current, { - messageType: "tool_return_message", + message_type: "tool_return_message", id: "dummy", - date: new Date(), - toolCallId, - toolReturn: toolResult.toolReturn, + date: new Date().toISOString(), + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, @@ -497,8 +502,8 @@ export default function App({ approvals: [ { type: "tool", - toolCallId, - toolReturn: toolResult.toolReturn, + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, @@ -664,6 +669,61 @@ export default function App({ return { submitted: true }; } + // Special handling for /clear command - reset conversation + if (msg.trim() === "/clear") { + const cmdId = uid("cmd"); + buffersRef.current.byId.set(cmdId, { + kind: "command", + id: cmdId, + input: msg, + output: "Clearing conversation...", + phase: "running", + }); + buffersRef.current.order.push(cmdId); + refreshDerived(); + + setCommandRunning(true); + + try { + const client = getClient(); + await client.agents.messages.reset(agentId, { + add_default_initial_messages: false, + }); + + // Clear local buffers and static items + // buffersRef.current.byId.clear(); + // buffersRef.current.order = []; + // buffersRef.current.tokenCount = 0; + // emittedIdsRef.current.clear(); + // setStaticItems([]); + + // Update command with success + buffersRef.current.byId.set(cmdId, { + kind: "command", + id: cmdId, + input: msg, + output: "Conversation cleared", + phase: "finished", + success: true, + }); + buffersRef.current.order.push(cmdId); + refreshDerived(); + } catch (error) { + buffersRef.current.byId.set(cmdId, { + kind: "command", + id: cmdId, + input: msg, + output: `Failed: ${error instanceof Error ? error.message : String(error)}`, + phase: "finished", + success: false, + }); + refreshDerived(); + } finally { + setCommandRunning(false); + } + return { submitted: true }; + } + // Immediately add command to transcript with "running" phase const cmdId = uid("cmd"); buffersRef.current.byId.set(cmdId, { @@ -782,8 +842,8 @@ export default function App({ // Start the conversation loop await processConversation([ { - role: Letta.MessageCreateRole.User, - content: messageContent as unknown as Letta.MessageCreate["content"], + role: "user", + content: messageContent as unknown as MessageCreate["content"], }, ]); @@ -817,11 +877,11 @@ export default function App({ // Update buffers with tool return onChunk(buffersRef.current, { - messageType: "tool_return_message", + message_type: "tool_return_message", id: "dummy", - date: new Date(), - toolCallId, - toolReturn: toolResult.toolReturn, + date: new Date().toISOString(), + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, @@ -837,8 +897,8 @@ export default function App({ approvals: [ { type: "tool", - toolCallId, - toolReturn: toolResult.toolReturn, + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, @@ -897,7 +957,7 @@ export default function App({ await processConversation([ { type: "approval", - approvalRequestId: toolCallId, + approval_request_id: toolCallId, approve: false, reason: reason || "User denied the tool execution", // TODO the above is legacy? @@ -1031,11 +1091,11 @@ export default function App({ // Update buffers with tool return onChunk(buffersRef.current, { - messageType: "tool_return_message", + message_type: "tool_return_message", id: "dummy", - date: new Date(), - toolCallId, - toolReturn: toolResult.toolReturn, + date: new Date().toISOString(), + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, @@ -1052,8 +1112,8 @@ export default function App({ approvals: [ { type: "tool", - toolCallId, - toolReturn: toolResult.toolReturn, + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, @@ -1085,7 +1145,7 @@ export default function App({ await processConversation([ { type: "approval", - approvalRequestId: toolCallId, + approval_request_id: toolCallId, approve: false, reason: reason || diff --git a/src/cli/commands/registry.ts b/src/cli/commands/registry.ts index 47fb4b2..de02a34 100644 --- a/src/cli/commands/registry.ts +++ b/src/cli/commands/registry.ts @@ -36,6 +36,13 @@ export const commands: Record = { return "Exiting..."; }, }, + "/clear": { + desc: "Clear conversation history", + handler: () => { + // Handled specially in App.tsx to access client and agent ID + return "Clearing messages..."; + }, + }, }; /** diff --git a/src/cli/helpers/accumulator.ts b/src/cli/helpers/accumulator.ts index 7d69ea5..7378ea6 100644 --- a/src/cli/helpers/accumulator.ts +++ b/src/cli/helpers/accumulator.ts @@ -4,7 +4,7 @@ // - Tool calls update in-place (same toolCallId for call+return). // - Exposes `onChunk` to feed SDK events and `toLines` to render. -import type { Letta } from "@letta-ai/letta-client"; +import type { LettaStreamingChunk } from "../../agent/message"; // One line per transcript row. Tool calls evolve in-place. // For tool call returns, merge into the tool call matching the toolCallId @@ -194,10 +194,7 @@ function extractTextPart(v: unknown): string { } // Feed one SDK chunk; mutate buffers in place. -export function onChunk( - b: Buffers, - chunk: Letta.agents.LettaStreamingResponse, -) { +export function onChunk(b: Buffers, chunk: LettaStreamingChunk) { // TODO remove once SDK v1 has proper typing for in-stream errors // Check for streaming error objects (not typed in SDK but emitted by backend) // These are emitted when LLM errors occur during streaming (rate limits, timeouts, etc.) @@ -283,20 +280,28 @@ export function onChunk( if (!id) break; - const toolCallId = chunk.toolCall?.toolCallId; - const name = chunk.toolCall?.name; - const argsText = chunk.toolCall?.arguments; + const toolCall = chunk.tool_call || (Array.isArray(chunk.tool_calls) && chunk.tool_calls.length > 0 ? chunk.tool_calls[0] : null); + const toolCallId = toolCall?.tool_call_id; + const name = toolCall?.name; + const argsText = toolCall?.arguments; // Record correlation: toolCallId → line id (otid) if (toolCallId) b.toolCallIdToLineId.set(toolCallId, id); */ let id = chunk.otid; - // console.log(`[TOOL_CALL] Received ${chunk.messageType} with otid=${id}, toolCallId=${chunk.toolCall?.toolCallId}, name=${chunk.toolCall?.name}`); + // console.log(`[TOOL_CALL] Received ${chunk.message_type} with otid=${id}, toolCallId=${chunk.tool_call?.tool_call_id}, name=${chunk.tool_call?.name}`); - const toolCallId = chunk.toolCall?.toolCallId; - const name = chunk.toolCall?.name; - const argsText = chunk.toolCall?.arguments; + // Use deprecated tool_call or new tool_calls array + const toolCall = + chunk.tool_call || + (Array.isArray(chunk.tool_calls) && chunk.tool_calls.length > 0 + ? chunk.tool_calls[0] + : null); + + const toolCallId = toolCall?.tool_call_id; + const name = toolCall?.name; + const argsText = toolCall?.arguments; // ========== START BACKEND BUG WORKAROUND (Remove after OTID fix) ========== // Bug: Backend sends same otid for reasoning and tool_call, and multiple otids for same tool_call @@ -310,7 +315,7 @@ export function onChunk( } // Handle otid transition for tracking purposes - handleOtidTransition(b, chunk.otid); + handleOtidTransition(b, chunk.otid ?? undefined); } else { // Check if this otid is already used by a reasoning line if (id && b.byId.has(id)) { @@ -327,7 +332,7 @@ export function onChunk( // This part stays after fix: // Handle otid transition (mark previous line as finished) // This must happen BEFORE the break, so reasoning gets finished even when tool has no otid - handleOtidTransition(b, id); + handleOtidTransition(b, id ?? undefined); if (!id) { // console.log(`[TOOL_CALL] No otid, breaking`); @@ -338,21 +343,24 @@ export function onChunk( if (toolCallId) b.toolCallIdToLineId.set(toolCallId, id); } + // Early exit if no valid id + if (!id) break; + const desiredPhase = - chunk.messageType === "approval_request_message" + chunk.message_type === "approval_request_message" ? "ready" : "streaming"; const line = ensure(b, id, () => ({ kind: "tool_call", id, - toolCallId: toolCallId, - name: name, + toolCallId: toolCallId ?? undefined, + name: name ?? undefined, phase: desiredPhase, })); // If this is an approval request and the line already exists, bump phase to ready if ( - chunk.messageType === "approval_request_message" && + chunk.message_type === "approval_request_message" && line.phase !== "finished" ) { b.byId.set(id, { ...line, phase: "ready" }); @@ -372,8 +380,8 @@ export function onChunk( case "tool_return_message": { // Tool return is a special case // It will have a different otid than the tool call, but we want to merge into the tool call - const toolCallId = chunk.toolCallId; - const resultText = chunk.toolReturn; + const toolCallId = chunk.tool_call_id; + const resultText = chunk.tool_return; const status = chunk.status; // Look up the line by toolCallId @@ -401,17 +409,17 @@ export function onChunk( case "usage_statistics": { // Accumulate usage statistics from the stream // These messages arrive after stop_reason in the stream - if (chunk.promptTokens !== undefined) { - b.usage.promptTokens += chunk.promptTokens; + if (chunk.prompt_tokens !== undefined) { + b.usage.promptTokens += chunk.prompt_tokens; } - if (chunk.completionTokens !== undefined) { - b.usage.completionTokens += chunk.completionTokens; + if (chunk.completion_tokens !== undefined) { + b.usage.completionTokens += chunk.completion_tokens; } - if (chunk.totalTokens !== undefined) { - b.usage.totalTokens += chunk.totalTokens; + if (chunk.total_tokens !== undefined) { + b.usage.totalTokens += chunk.total_tokens; } - if (chunk.stepCount !== undefined) { - b.usage.stepCount += chunk.stepCount; + if (chunk.step_count !== undefined) { + b.usage.stepCount += chunk.step_count; } break; } diff --git a/src/cli/helpers/backfill.ts b/src/cli/helpers/backfill.ts index 7f75a83..e0fe4f0 100644 --- a/src/cli/helpers/backfill.ts +++ b/src/cli/helpers/backfill.ts @@ -1,4 +1,8 @@ -import type { Letta } from "@letta-ai/letta-client"; +import type { + LettaAssistantMessageContentUnion, + LettaMessageUnion, + LettaUserMessageContentUnion, +} from "@letta-ai/letta-client/resources/agents/messages"; import type { Buffers } from "./accumulator"; // const PASTE_LINE_THRESHOLD = 5; @@ -16,7 +20,7 @@ function clip(s: string, limit: number): string { } function renderAssistantContentParts( - parts: Letta.AssistantMessageContent, + parts: string | LettaAssistantMessageContentUnion[], ): string { // AssistantContent can be a string or an array of text parts if (typeof parts === "string") return parts; @@ -29,7 +33,9 @@ function renderAssistantContentParts( return out; } -function renderUserContentParts(parts: Letta.UserMessageContent): string { +function renderUserContentParts( + parts: string | LettaUserMessageContentUnion[], +): string { // UserContent can be a string or an array of text OR image parts // for text parts, we clip them if they're too big (eg copy-pasted chunks) // for image parts, we just show a placeholder @@ -49,7 +55,7 @@ function renderUserContentParts(parts: Letta.UserMessageContent): string { export function backfillBuffers( buffers: Buffers, - history: Letta.LettaMessageUnion[], + history: LettaMessageUnion[], ): void { // Clear buffers to ensure idempotency (in case this is called multiple times) buffers.order = []; @@ -65,7 +71,7 @@ export function backfillBuffers( // Use otid as line ID when available (like streaming does), fall back to msg.id const lineId = "otid" in msg && msg.otid ? msg.otid : msg.id; - switch (msg.messageType) { + switch (msg.message_type) { // user message - content parts may include text and image parts case "user_message": { const exists = buffers.byId.has(lineId); @@ -107,9 +113,16 @@ export function backfillBuffers( // tool call message OR approval request (they're the same in history) case "tool_call_message": case "approval_request_message": { - if ("toolCall" in msg && msg.toolCall?.toolCallId) { - const toolCall = msg.toolCall; - const toolCallId = toolCall.toolCallId; + // Use tool_calls array (new) or fallback to tool_call (deprecated) + const toolCalls = Array.isArray(msg.tool_calls) + ? msg.tool_calls + : msg.tool_call + ? [msg.tool_call] + : []; + + if (toolCalls.length > 0 && toolCalls[0]?.tool_call_id) { + const toolCall = toolCalls[0]; + const toolCallId = toolCall.tool_call_id; const exists = buffers.byId.has(lineId); buffers.byId.set(lineId, { @@ -130,7 +143,7 @@ export function backfillBuffers( // tool return message - merge into the existing tool call line case "tool_return_message": { - const toolCallId = msg.toolCallId; + const toolCallId = msg.tool_call_id; if (!toolCallId) break; // Look up the line using the mapping (like streaming does) @@ -143,7 +156,7 @@ export function backfillBuffers( // Update the existing line with the result buffers.byId.set(toolCallLineId, { ...existingLine, - resultText: msg.toolReturn, + resultText: msg.tool_return, resultOk: msg.status === "success", phase: "finished", }); diff --git a/src/cli/helpers/stream.ts b/src/cli/helpers/stream.ts index e59a9c4..b2040a4 100644 --- a/src/cli/helpers/stream.ts +++ b/src/cli/helpers/stream.ts @@ -1,4 +1,7 @@ -import { Letta } from "@letta-ai/letta-client"; +import type { Stream } from "@letta-ai/letta-client/core/streaming"; +import type { LettaStreamingResponse } from "@letta-ai/letta-client/resources/agents/messages"; +import type { StopReasonType } from "@letta-ai/letta-client/resources/runs/runs"; + import { type createBuffers, markCurrentLineAsFinished, @@ -13,7 +16,7 @@ export type ApprovalRequest = { }; type DrainResult = { - stopReason: Letta.StopReasonType; + stopReason: StopReasonType; lastRunId?: string | null; lastSeqId?: number | null; approval?: ApprovalRequest | null; // present only if we ended due to approval @@ -21,7 +24,7 @@ type DrainResult = { }; export async function drainStream( - stream: AsyncIterable, + stream: Stream, buffers: ReturnType, refresh: () => void, abortSignal?: AbortSignal, @@ -33,29 +36,36 @@ export async function drainStream( let toolName: string | null = null; let toolArgs: string | null = null; - let stopReason: Letta.StopReasonType | null = null; + let stopReason: StopReasonType | null = null; let lastRunId: string | null = null; let lastSeqId: number | null = null; for await (const chunk of stream) { + // console.log("chunk", chunk); + // Check if stream was aborted if (abortSignal?.aborted) { - stopReason = "cancelled" as Letta.StopReasonType; + stopReason = "cancelled"; // Mark incomplete tool calls as cancelled to prevent stuck blinking UI markIncompleteToolsAsCancelled(buffers); queueMicrotask(refresh); break; } - // Store the runId and seqId to re-connect if stream is interrupted - if ("runId" in chunk && "seqId" in chunk && chunk.runId && chunk.seqId) { - lastRunId = chunk.runId; - lastSeqId = chunk.seqId; + // Store the run_id and seq_id to re-connect if stream is interrupted + if ( + "run_id" in chunk && + "seq_id" in chunk && + chunk.run_id && + chunk.seq_id + ) { + lastRunId = chunk.run_id; + lastSeqId = chunk.seq_id; } - if (chunk.messageType === "ping") continue; + if (chunk.message_type === "ping") continue; // Need to store the approval request ID to send an approval in a new run - if (chunk.messageType === "approval_request_message") { + if (chunk.message_type === "approval_request_message") { approvalRequestId = chunk.id; } @@ -63,25 +73,32 @@ export async function drainStream( // in both the onChunk handler and here, we could refactor to instead pull the tool name // and JSON args from the mutated lines (eg last mutated line) if ( - chunk.messageType === "tool_call_message" || - chunk.messageType === "approval_request_message" + chunk.message_type === "tool_call_message" || + chunk.message_type === "approval_request_message" ) { - if (chunk.toolCall?.toolCallId) { - toolCallId = chunk.toolCall.toolCallId; + // Use deprecated tool_call or new tool_calls array + const toolCall = + chunk.tool_call || + (Array.isArray(chunk.tool_calls) && chunk.tool_calls.length > 0 + ? chunk.tool_calls[0] + : null); + + if (toolCall?.tool_call_id) { + toolCallId = toolCall.tool_call_id; } - if (chunk.toolCall?.name) { + if (toolCall?.name) { if (toolName) { // TODO would expect that we should allow stacking? I guess not? - // toolName = toolName + chunk.toolCall.name; + // toolName = toolName + toolCall.name; } else { - toolName = chunk.toolCall.name; + toolName = toolCall.name; } } - if (chunk.toolCall?.arguments) { + if (toolCall?.arguments) { if (toolArgs) { - toolArgs = toolArgs + chunk.toolCall.arguments; + toolArgs = toolArgs + toolCall.arguments; } else { - toolArgs = chunk.toolCall.arguments; + toolArgs = toolCall.arguments; } } } @@ -89,15 +106,15 @@ export async function drainStream( onChunk(buffers, chunk); queueMicrotask(refresh); - if (chunk.messageType === "stop_reason") { - stopReason = chunk.stopReason; + if (chunk.message_type === "stop_reason") { + stopReason = chunk.stop_reason; // Continue reading stream to get usage_statistics that may come after } } // Stream has ended, check if we captured a stop reason if (!stopReason) { - stopReason = Letta.StopReasonType.Error; + stopReason = "error"; } // Mark the final line as finished now that stream has ended diff --git a/src/headless.ts b/src/headless.ts index b71aa63..d5c6711 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -1,5 +1,9 @@ import { parseArgs } from "node:util"; -import { Letta } from "@letta-ai/letta-client"; +import type { + AgentState, + MessageCreate, +} from "@letta-ai/letta-client/resources/agents/agents"; +import type { ApprovalCreate } from "@letta-ai/letta-client/resources/agents/messages"; import { getClient } from "./agent/client"; import { createAgent } from "./agent/create"; import { sendMessageStream } from "./agent/message"; @@ -49,7 +53,7 @@ export async function handleHeadlessCommand(argv: string[]) { const client = await getClient(); // Resolve agent (same logic as interactive mode) - let agent: Letta.AgentState | null = null; + let agent: AgentState | null = null; const specifiedAgentId = values.agent as string | undefined; const shouldContinue = values.continue as boolean | undefined; const forceNew = values.new as boolean | undefined; @@ -132,9 +136,9 @@ export async function handleHeadlessCommand(argv: string[]) { } // Send message and process stream loop - let currentInput: Array = [ + let currentInput: Array = [ { - role: Letta.MessageCreateRole.User, + role: "user", content: [{ type: "text", text: prompt }], }, ]; @@ -218,12 +222,12 @@ export async function handleHeadlessCommand(argv: string[]) { sessionStats.endTurn(apiDurationMs); // Case 1: Turn ended normally - if (stopReason === Letta.StopReasonType.EndTurn) { + if (stopReason === "end_turn") { break; } // Case 2: Requires approval - if (stopReason === Letta.StopReasonType.RequiresApproval) { + if (stopReason === "requires_approval") { if (!approval) { console.error("Unexpected null approval"); process.exit(1); @@ -244,7 +248,7 @@ export async function handleHeadlessCommand(argv: string[]) { currentInput = [ { type: "approval", - approvalRequestId: toolCallId, + approval_request_id: toolCallId, approve: false, reason: denyReason, }, @@ -257,7 +261,7 @@ export async function handleHeadlessCommand(argv: string[]) { currentInput = [ { type: "approval", - approvalRequestId: toolCallId, + approval_request_id: toolCallId, approve: false, reason: "Tool requires approval (headless mode)", }, @@ -274,8 +278,8 @@ export async function handleHeadlessCommand(argv: string[]) { approvals: [ { type: "tool", - toolCallId, - toolReturn: toolResult.toolReturn, + tool_call_id: toolCallId, + tool_return: toolResult.toolReturn, status: toolResult.status, stdout: toolResult.stdout, stderr: toolResult.stderr, diff --git a/src/index.ts b/src/index.ts index 2e4840f..d03c72f 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ #!/usr/bin/env bun import { parseArgs } from "node:util"; -import type { Letta } from "@letta-ai/letta-client"; +import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents"; import { getResumeData, type ResumeData } from "./agent/check-approval"; import { getClient } from "./agent/client"; import { permissionMode } from "./permissions/mode"; @@ -216,7 +216,7 @@ async function main() { const { updateSettings, loadProjectSettings, updateProjectSettings } = await import("./settings"); - let agent: Letta.AgentState | null = null; + let agent: AgentState | null = null; // Priority 1: Try to use --agent specified ID if (agentIdArg) { diff --git a/src/tests/message.smoke.ts b/src/tests/message.smoke.ts index c0fd917..8649768 100755 --- a/src/tests/message.smoke.ts +++ b/src/tests/message.smoke.ts @@ -18,19 +18,21 @@ async function main() { console.log(`✅ Agent created: ${agent.id}`); console.log("💬 Sending test message..."); - const stream = await sendMessageStream( - agent.id, - "Hello from Bun smoke test! Try calling a tool.", - ); + const stream = await sendMessageStream(agent.id, [ + { + role: "user", + content: "Hello from Bun smoke test! Try calling a tool.", + }, + ]); // Print every chunk as it arrives for await (const chunk of stream) { - const type = chunk.messageType ?? "unknown"; + const type = chunk.message_type ?? "unknown"; - switch (chunk.messageType) { + switch (chunk.message_type) { case "reasoning_message": { - const run = chunk.runId - ? `run=${chunk.runId}:${chunk.seqId ?? "-"} ` + const run = chunk.run_id + ? `run=${chunk.run_id}:${chunk.seq_id ?? "-"} ` : ""; process.stdout.write( `[reasoning] ${run}${JSON.stringify(chunk) ?? ""}\n`, @@ -38,8 +40,8 @@ async function main() { break; } case "assistant_message": { - const run = chunk.runId - ? `run=${chunk.runId}:${chunk.seqId ?? "-"} ` + const run = chunk.run_id + ? `run=${chunk.run_id}:${chunk.seq_id ?? "-"} ` : ""; process.stdout.write( `[assistant] ${run}${JSON.stringify(chunk) ?? ""}\n`, @@ -47,8 +49,8 @@ async function main() { break; } case "tool_call_message": { - const run = chunk.runId - ? `run=${chunk.runId}:${chunk.seqId ?? "-"} ` + const run = chunk.run_id + ? `run=${chunk.run_id}:${chunk.seq_id ?? "-"} ` : ""; process.stdout.write( `[tool_call] ${run}${JSON.stringify(chunk) ?? ""}\n`, @@ -56,15 +58,15 @@ async function main() { break; } case "tool_return_message": { - const run = chunk.runId - ? `run=${chunk.runId}:${chunk.seqId ?? "-"} ` + const run = chunk.run_id + ? `run=${chunk.run_id}:${chunk.seq_id ?? "-"} ` : ""; process.stdout.write(`[tool_return] ${run}${chunk}\n`); break; } case "approval_request_message": { - const run = chunk.runId - ? `run=${chunk.runId}:${chunk.seqId ?? "-"} ` + const run = chunk.run_id + ? `run=${chunk.run_id}:${chunk.seq_id ?? "-"} ` : ""; process.stdout.write( `[approval_request] ${run}${JSON.stringify(chunk)}\n`, diff --git a/src/tools/manager.ts b/src/tools/manager.ts index c3742b2..f4fb5fb 100644 --- a/src/tools/manager.ts +++ b/src/tools/manager.ts @@ -1,4 +1,4 @@ -import type { LettaClient } from "@letta-ai/letta-client"; +import type Letta from "@letta-ai/letta-client"; import { TOOL_DEFINITIONS, type ToolName } from "./toolDefinitions"; export const TOOL_NAMES = Object.keys(TOOL_DEFINITIONS) as ToolName[]; @@ -228,7 +228,7 @@ export async function loadTools(): Promise { * @param client - Letta client instance * @returns Promise that resolves when all tools are registered */ -export async function upsertToolsToServer(client: LettaClient): Promise { +export async function upsertToolsToServer(client: Letta): Promise { const upsertPromises = Array.from(toolRegistry.entries()).map( async ([name, tool]) => { const pythonStub = generatePythonStub( @@ -245,9 +245,9 @@ export async function upsertToolsToServer(client: LettaClient): Promise { }; await client.tools.upsert({ - defaultRequiresApproval: true, - sourceCode: pythonStub, - jsonSchema: fullJsonSchema, + default_requires_approval: true, + source_code: pythonStub, + json_schema: fullJsonSchema, // description: tool.schema.description, // tags: ['client-side', 'typescript'], });