@sectr/copilotkit 0.0.8
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/LICENSE +21 -0
- package/README.md +86 -0
- package/dist/agent.d.ts +7 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +72 -0
- package/dist/agent.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +85 -0
- package/dist/runner.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Donald Pinckney
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @sectr/copilotkit
|
|
2
|
+
|
|
3
|
+
Run [CopilotKit](https://docs.copilotkit.ai) on [Sectr](https://sectr.dev).
|
|
4
|
+
Every Sectr route is an [AG-UI](https://docs.ag-ui.com) endpoint, so
|
|
5
|
+
CopilotKit's chat components talk to your agent as is. This package adds the
|
|
6
|
+
two pieces that keep conversations on Sectr:
|
|
7
|
+
|
|
8
|
+
- `SectrAgent`: the AG-UI agent for a Sectr route. It's `@ag-ui/client`'s
|
|
9
|
+
`HttpAgent`, plus reconnecting a reloaded page to its conversation.
|
|
10
|
+
- `SectrRunner`: the Copilot Runtime runner that keeps conversations on
|
|
11
|
+
Sectr instead of in your server's memory.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @sectr/copilotkit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
It uses your app's own `@copilotkit/runtime` (1.77 or later), `@ag-ui/client`
|
|
18
|
+
and `rxjs`, which CopilotKit apps already have.
|
|
19
|
+
|
|
20
|
+
## Use it: through the Copilot Runtime (recommended)
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// app/api/copilotkit/[[...slug]]/route.ts
|
|
24
|
+
import { CopilotRuntime, createCopilotRuntimeHandler } from "@copilotkit/runtime/v2";
|
|
25
|
+
import { SectrAgent, SectrRunner } from "@sectr/copilotkit";
|
|
26
|
+
|
|
27
|
+
const sectrAgent = new SectrAgent({ url: process.env.SECTR_AGUI_URL! });
|
|
28
|
+
const agents = { default: sectrAgent };
|
|
29
|
+
const runtime = new CopilotRuntime({ agents, runner: new SectrRunner({ agents }) });
|
|
30
|
+
const handler = createCopilotRuntimeHandler({ runtime, basePath: "/api/copilotkit" });
|
|
31
|
+
|
|
32
|
+
export const GET = handler;
|
|
33
|
+
export const POST = handler;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`SECTR_AGUI_URL` is the AG-UI URL that `sectr dev` and `sectr deploy` print
|
|
37
|
+
for your route. `SectrRunner` needs `SectrAgent`s in its agents map: it
|
|
38
|
+
reconnects through them.
|
|
39
|
+
|
|
40
|
+
## What it does
|
|
41
|
+
|
|
42
|
+
The Runtime's runner decides where conversations live. CopilotKit's default
|
|
43
|
+
keeps a copy of every thread in your server's memory and serves it back to
|
|
44
|
+
any caller. `SectrRunner` keeps nothing:
|
|
45
|
+
|
|
46
|
+
- **Runs** stream straight from Sectr: one request to your route's AG-UI
|
|
47
|
+
endpoint, with the user's token (the Runtime forwards `Authorization`).
|
|
48
|
+
- **Reconnects** come from Sectr too, through the `SectrAgent`. A reloaded
|
|
49
|
+
page gets the conversation so far, plus its latest run: still streaming if
|
|
50
|
+
the agent is mid-turn, or ending in the approval that's waiting for a
|
|
51
|
+
decision.
|
|
52
|
+
- **Errors** (a refused request, a broken stream) end the run with a
|
|
53
|
+
`RUN_ERROR` instead of leaving the chat spinning.
|
|
54
|
+
|
|
55
|
+
Your server stays stateless, so deploy it anywhere: serverless, several
|
|
56
|
+
instances, restarts. The only copy of a conversation is the one Sectr shows
|
|
57
|
+
only to its owner.
|
|
58
|
+
|
|
59
|
+
## Use it: direct from the browser
|
|
60
|
+
|
|
61
|
+
For prototypes, give CopilotKit the agent itself, with no Runtime:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { SectrAgent } from "@sectr/copilotkit";
|
|
65
|
+
|
|
66
|
+
const agent = new SectrAgent({
|
|
67
|
+
url: "https://api.sectr.dev/ag-ui/support-agent/chat",
|
|
68
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
<CopilotKitProvider selfManagedAgents={{ default: agent }}>
|
|
72
|
+
<CopilotChat threadId={threadId} />
|
|
73
|
+
</CopilotKitProvider>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A reloaded page reconnects the same way: `SectrAgent` implements AG-UI's
|
|
77
|
+
`connect()`, which a plain `HttpAgent` leaves out, so with a plain
|
|
78
|
+
`HttpAgent` a reloaded page starts empty and a pending approval can't be
|
|
79
|
+
answered.
|
|
80
|
+
|
|
81
|
+
The full guide, including user tokens and approvals:
|
|
82
|
+
[Connecting your frontend](https://docs.sectr.dev/getting-started/connecting-frontend/).
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HttpAgent, type BaseEvent, type RunAgentInput } from "@ag-ui/client";
|
|
2
|
+
import { Observable } from "rxjs";
|
|
3
|
+
export declare class SectrAgent extends HttpAgent {
|
|
4
|
+
protected connect(input: RunAgentInput): Observable<BaseEvent>;
|
|
5
|
+
connectThread(threadId: string, headers?: Record<string, string>): Observable<BaseEvent>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EAGT,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAwB,UAAU,EAAc,MAAM,MAAM,CAAC;AAYpE,qBAAa,UAAW,SAAQ,SAAS;IACvC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC,SAAS,CAAC;IAM9D,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,UAAU,CAAC,SAAS,CAAC;CAmB7F"}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// SectrAgent: an AG-UI HttpAgent for a Sectr route that can also reconnect.
|
|
2
|
+
import { HttpAgent, runHttpRequest, transformHttpEventStream, } from "@ag-ui/client";
|
|
3
|
+
import { concat, defer, EMPTY, Observable, throwError } from "rxjs";
|
|
4
|
+
// `HttpAgent` plus AG-UI's `connect()`: a reloaded page reattaches to its
|
|
5
|
+
// conversation through Sectr's read-only endpoint (`GET <route>?threadId=`).
|
|
6
|
+
// It gets the thread's history plus its latest run replayed: still streaming
|
|
7
|
+
// if the agent is mid-turn, or ending in the approval that's waiting for a
|
|
8
|
+
// decision. A new thread has no run, so the stream is empty. (`HttpAgent`
|
|
9
|
+
// leaves `connect()` unimplemented, so a reloaded page used to start empty.)
|
|
10
|
+
//
|
|
11
|
+
// Use it wherever you'd use an `HttpAgent` for a Sectr route: in the browser
|
|
12
|
+
// for direct mode (`selfManagedAgents`), or in the Copilot Runtime's agents
|
|
13
|
+
// map, where `SectrRunner` reconnects through it.
|
|
14
|
+
export class SectrAgent extends HttpAgent {
|
|
15
|
+
connect(input) {
|
|
16
|
+
return this.connectThread(input.threadId);
|
|
17
|
+
}
|
|
18
|
+
// The reconnect stream for `threadId`, with this agent's headers plus
|
|
19
|
+
// `headers` (`SectrRunner` passes the browser's forwarded `Authorization`).
|
|
20
|
+
connectThread(threadId, headers = {}) {
|
|
21
|
+
return defer(() => {
|
|
22
|
+
const url = new URL(this.url);
|
|
23
|
+
url.searchParams.set("threadId", threadId);
|
|
24
|
+
const merged = new Headers(this.headers);
|
|
25
|
+
for (const [name, value] of Object.entries(headers))
|
|
26
|
+
merged.set(name, value);
|
|
27
|
+
merged.set("Accept", "text/event-stream");
|
|
28
|
+
let broken;
|
|
29
|
+
const stream = transformHttpEventStream(runHttpRequest(async () => quietBody(await this.fetch(url.toString(), { method: "GET", headers: merged }), (error) => (broken = { error }))));
|
|
30
|
+
return concat(stream, defer(() => (broken ? throwError(() => broken.error) : EMPTY)));
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// @ag-ui/client (1.0.1) cancels the response body when the stream ends and
|
|
35
|
+
// rethrows anything but an AbortError from that cancel. Cancelling a fetch
|
|
36
|
+
// body that broke, or that is cancelled mid-read (a page reloading mid-turn),
|
|
37
|
+
// rejects with undici's "terminated", so that escapes as an unhandled
|
|
38
|
+
// rejection, which crashes a plain Node server. So it gets a body that never
|
|
39
|
+
// errors and whose cancel never rejects: a broken stream just ends, and
|
|
40
|
+
// `onError` lets the caller raise the error after it.
|
|
41
|
+
function quietBody(response, onError) {
|
|
42
|
+
if (!response.ok || !response.body)
|
|
43
|
+
return response;
|
|
44
|
+
const reader = response.body.getReader();
|
|
45
|
+
const body = new ReadableStream({
|
|
46
|
+
async pull(controller) {
|
|
47
|
+
try {
|
|
48
|
+
const { done, value } = await reader.read();
|
|
49
|
+
if (done)
|
|
50
|
+
controller.close();
|
|
51
|
+
else
|
|
52
|
+
controller.enqueue(value);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
onError(error);
|
|
56
|
+
try {
|
|
57
|
+
controller.close();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Already cancelled: nobody is reading.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
cancel: (reason) => reader.cancel(reason).catch(() => { }),
|
|
65
|
+
});
|
|
66
|
+
return new Response(body, {
|
|
67
|
+
status: response.status,
|
|
68
|
+
statusText: response.statusText,
|
|
69
|
+
headers: response.headers,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @sectr/copilotkit — run CopilotKit on Sectr.
|
|
2
|
+
//
|
|
3
|
+
// `SectrAgent` is the AG-UI agent for a Sectr route: it runs turns and
|
|
4
|
+
// reconnects a reloaded page. Hand `SectrRunner` to your Copilot Runtime and
|
|
5
|
+
// every run and reconnect streams from Sectr with the user's token; or use a
|
|
6
|
+
// `SectrAgent` straight from the browser (direct mode).
|
|
7
|
+
// Guide: https://docs.sectr.dev/getting-started/connecting-frontend/
|
|
8
|
+
export { SectrAgent } from "./agent.js";
|
|
9
|
+
export { SectrRunner } from "./runner.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,uEAAuE;AACvE,6EAA6E;AAC7E,6EAA6E;AAC7E,wDAAwD;AACxD,qEAAqE;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type BaseEvent } from "@ag-ui/client";
|
|
2
|
+
import { AgentRunner, type AgentRunnerConnectRequest, type AgentRunnerRunRequest } from "@copilotkit/runtime/v2";
|
|
3
|
+
import { Observable } from "rxjs";
|
|
4
|
+
import type { SectrAgent } from "./agent.js";
|
|
5
|
+
export declare class SectrRunner extends AgentRunner {
|
|
6
|
+
private readonly options;
|
|
7
|
+
constructor(options: {
|
|
8
|
+
agents: Record<string, SectrAgent>;
|
|
9
|
+
});
|
|
10
|
+
run({ agent, input }: AgentRunnerRunRequest): Observable<BaseEvent>;
|
|
11
|
+
connect({ threadId, agentId, headers }: AgentRunnerConnectRequest): Observable<BaseEvent>;
|
|
12
|
+
isRunning(): Promise<boolean>;
|
|
13
|
+
stop(): Promise<boolean>;
|
|
14
|
+
private agentFor;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EACL,WAAW,EAEX,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAS,UAAU,EAAE,MAAM,MAAM,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAQ7C,qBAAa,WAAY,SAAQ,WAAW;IAG9B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;KAAE;IAM5E,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,qBAAqB,GAAG,UAAU,CAAC,SAAS,CAAC;IAenE,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC;IAOnF,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAI9B,OAAO,CAAC,QAAQ;CAYjB"}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// SectrRunner: the stateless Copilot Runtime runner.
|
|
2
|
+
import { EventType } from "@ag-ui/client";
|
|
3
|
+
import { AgentRunner, createRunEventFinalizer, } from "@copilotkit/runtime/v2";
|
|
4
|
+
import { defer, Observable } from "rxjs";
|
|
5
|
+
// A stateless Copilot Runtime runner: Sectr's journal is the conversation, so
|
|
6
|
+
// this server keeps no copy of it. (CopilotKit's default in-memory runner
|
|
7
|
+
// keeps every thread in process memory and serves it back to any caller.)
|
|
8
|
+
// Every read and write goes to Sectr with the user's token, and Sectr only
|
|
9
|
+
// shows a user their own threads. Deploy it anywhere: serverless, several
|
|
10
|
+
// instances, restarts all fine.
|
|
11
|
+
export class SectrRunner extends AgentRunner {
|
|
12
|
+
options;
|
|
13
|
+
// The same agents map you pass to CopilotRuntime: connect() reconnects
|
|
14
|
+
// through its agents, so they must be `SectrAgent`s.
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super();
|
|
17
|
+
this.options = options;
|
|
18
|
+
}
|
|
19
|
+
// A run streams straight from Sectr: one POST to the route's AG-UI endpoint
|
|
20
|
+
// (the Runtime has already forwarded the browser's `Authorization`).
|
|
21
|
+
run({ agent, input }) {
|
|
22
|
+
const events = new Observable((subscriber) => {
|
|
23
|
+
agent
|
|
24
|
+
.runAgent(input, { onEvent: ({ event }) => subscriber.next(event) })
|
|
25
|
+
.then(() => subscriber.complete(), (error) => subscriber.error(error));
|
|
26
|
+
return () => agent.abortRun();
|
|
27
|
+
});
|
|
28
|
+
return closeWithRunError(events, { protocolVersion: input.protocolVersion });
|
|
29
|
+
}
|
|
30
|
+
// A reloaded page reattaches to its conversation from Sectr, through the
|
|
31
|
+
// agent's `connectThread` (see SectrAgent) with the user's forwarded token.
|
|
32
|
+
connect({ threadId, agentId, headers }) {
|
|
33
|
+
const events = defer(() => this.agentFor(agentId).connectThread(threadId, headers));
|
|
34
|
+
return closeWithRunError(events, { emptyIsOk: true });
|
|
35
|
+
}
|
|
36
|
+
// The turn's state lives on Sectr (a busy thread answers 409), and Sectr
|
|
37
|
+
// has no cancel: closing the stream never stops the turn.
|
|
38
|
+
async isRunning() {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
async stop() {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
agentFor(agentId = "default") {
|
|
45
|
+
const agent = this.options.agents[agentId];
|
|
46
|
+
if (!agent)
|
|
47
|
+
throw new Error(`SectrRunner: no agent ${JSON.stringify(agentId)} in its agents map`);
|
|
48
|
+
// Duck-typed, not `instanceof`: a second copy of this package must still pass.
|
|
49
|
+
if (typeof agent.connectThread !== "function") {
|
|
50
|
+
throw new Error(`SectrRunner: agent ${JSON.stringify(agentId)} is a plain HttpAgent; ` +
|
|
51
|
+
"use SectrAgent from @sectr/copilotkit so a reloaded page can reconnect");
|
|
52
|
+
}
|
|
53
|
+
return agent;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// If Sectr refuses the request (401/404/409) or the stream breaks, end it
|
|
57
|
+
// with a RUN_ERROR so the chat shows the error instead of spinning or staying
|
|
58
|
+
// silently empty. `emptyIsOk`: a stream that completes without ever starting
|
|
59
|
+
// a run is fine as is (connect on a new thread).
|
|
60
|
+
function closeWithRunError(events, { protocolVersion, emptyIsOk = false }) {
|
|
61
|
+
return new Observable((subscriber) => {
|
|
62
|
+
const finalizer = createRunEventFinalizer();
|
|
63
|
+
let started = false;
|
|
64
|
+
const finish = (interruptionMessage) => {
|
|
65
|
+
const closers = finalizer.finalize({
|
|
66
|
+
...(protocolVersion !== undefined ? { protocolVersion } : {}),
|
|
67
|
+
...(interruptionMessage !== undefined ? { interruptionMessage } : {}),
|
|
68
|
+
});
|
|
69
|
+
for (const event of closers)
|
|
70
|
+
subscriber.next(event);
|
|
71
|
+
subscriber.complete();
|
|
72
|
+
};
|
|
73
|
+
const subscription = events.subscribe({
|
|
74
|
+
next: (event) => {
|
|
75
|
+
started ||= event.type === EventType.RUN_STARTED;
|
|
76
|
+
finalizer.observe(event);
|
|
77
|
+
subscriber.next(event);
|
|
78
|
+
},
|
|
79
|
+
error: (error) => finish(error instanceof Error ? error.message : String(error)),
|
|
80
|
+
complete: () => (started || !emptyIsOk ? finish() : subscriber.complete()),
|
|
81
|
+
});
|
|
82
|
+
return () => subscription.unsubscribe();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sectr/copilotkit",
|
|
3
|
+
"version": "0.0.8",
|
|
4
|
+
"description": "Run CopilotKit on Sectr: an AG-UI agent that reconnects reloaded pages, and a stateless Copilot Runtime runner, both backed by your Sectr agent.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"copilotkit",
|
|
7
|
+
"ag-ui",
|
|
8
|
+
"sectr",
|
|
9
|
+
"agents"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://docs.sectr.dev/getting-started/connecting-frontend/",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/sectr-dev/sectr.git",
|
|
15
|
+
"directory": "js/copilotkit"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Donald Pinckney",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.19"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
40
|
+
"test": "npm run build && node --test test/*.test.mjs"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@ag-ui/client": "^1.0.1",
|
|
44
|
+
"@copilotkit/runtime": ">=1.77.0 <2",
|
|
45
|
+
"rxjs": "^7.8.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@ag-ui/client": "1.0.1",
|
|
49
|
+
"@copilotkit/runtime": "1.77.0",
|
|
50
|
+
"@types/node": "^20.19.0",
|
|
51
|
+
"rxjs": "7.8.1",
|
|
52
|
+
"typescript": "5.9.3"
|
|
53
|
+
}
|
|
54
|
+
}
|