@factory/droid-sdk 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -482
- package/dist/chunk-4G2RKM2H.js +25831 -0
- package/dist/chunk-4G2RKM2H.js.map +1 -0
- package/dist/chunk-ROFAQUG4.mjs +25831 -0
- package/dist/chunk-ROFAQUG4.mjs.map +1 -0
- package/dist/{index-CwXX5IUH.d.cts → index-_nUPGHpH.d.mts} +92630 -79659
- package/dist/{index-CwXX5IUH.d.ts → index-_nUPGHpH.d.ts} +92630 -79659
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -93103
- package/dist/index.js +829 -11745
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +830 -0
- package/dist/index.mjs.map +1 -0
- package/dist/node.d.mts +757 -0
- package/dist/node.d.ts +757 -0
- package/dist/node.js +4731 -0
- package/dist/node.js.map +1 -0
- package/dist/node.mjs +4731 -0
- package/dist/node.mjs.map +1 -0
- package/docs/typescript-sdk-reference.md +1437 -0
- package/examples/browser/daemon-lifecycle.ts +95 -0
- package/examples/browser/daemon-model-discovery.ts +47 -0
- package/examples/browser/daemon-multi-session.ts +66 -0
- package/examples/browser/daemon-session.ts +73 -0
- package/examples/browser/index.html +69 -0
- package/examples/browser/list-sessions.ts +50 -0
- package/examples/node/abort-session-stream.ts +34 -0
- package/examples/node/ask-user-elicitation.ts +39 -0
- package/examples/node/compact-session.ts +63 -0
- package/examples/node/custom-system-prompt.ts +35 -0
- package/examples/node/document-input.ts +58 -0
- package/examples/node/enter-spec-mode.ts +87 -0
- package/examples/node/external-mcp.ts +45 -0
- package/examples/node/fork-session.ts +56 -0
- package/examples/node/hook-execution.ts +94 -0
- package/examples/node/image-attachment.ts +41 -0
- package/examples/node/init-metadata.ts +24 -0
- package/examples/node/interrupt-session.ts +55 -0
- package/examples/node/list-sessions.ts +20 -0
- package/examples/node/model-discovery.ts +28 -0
- package/examples/node/model-selection.ts +35 -0
- package/examples/node/multi-turn-session.ts +37 -0
- package/examples/node/observability.ts +75 -0
- package/examples/node/partial-stream.ts +56 -0
- package/examples/node/permission-handler.ts +60 -0
- package/examples/node/rename-session.ts +33 -0
- package/examples/node/result-metadata.ts +43 -0
- package/examples/node/resume-session.ts +41 -0
- package/examples/node/rewind-session.ts +79 -0
- package/examples/node/run.ts +32 -0
- package/examples/node/sdk-mcp-tool.ts +56 -0
- package/examples/node/session-settings.ts +35 -0
- package/examples/node/session-stream.ts +59 -0
- package/examples/node/skills.ts +24 -0
- package/examples/node/spec-mode-new-session.ts +120 -0
- package/examples/node/spec-mode-same-session.ts +119 -0
- package/examples/node/structured-output.ts +96 -0
- package/examples/node/tool-controls.ts +26 -0
- package/examples/package.json +3 -0
- package/package.json +20 -42
- package/dist/index.cjs +0 -12135
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -93103
- package/dist/protocol/index.cjs +0 -6466
- package/dist/protocol/index.cjs.map +0 -1
- package/dist/protocol/index.d.cts +0 -2
- package/dist/protocol/index.d.ts +0 -2
- package/dist/protocol/index.js +0 -6078
- package/dist/protocol/index.js.map +0 -1
- package/docs/daemon-usage-guide.md +0 -544
- package/docs/sdk-usage-guide.md +0 -748
- package/examples/abort-session-stream.ts +0 -34
- package/examples/daemon-multi-session.ts +0 -67
- package/examples/droid-dev-structured-output.ts +0 -44
- package/examples/fork-session.ts +0 -68
- package/examples/hook-execution.ts +0 -68
- package/examples/init-metadata.ts +0 -24
- package/examples/interrupt-session.ts +0 -35
- package/examples/list-sessions.ts +0 -19
- package/examples/multi-turn-session.ts +0 -40
- package/examples/permission-handler.ts +0 -41
- package/examples/readme-structured-output.ts +0 -26
- package/examples/result-metadata.ts +0 -63
- package/examples/run.ts +0 -39
- package/examples/sdk-mcp-tool.ts +0 -44
- package/examples/session-stream.ts +0 -57
- package/examples/spec-mode-new-session.ts +0 -61
- package/examples/spec-mode-same-session.ts +0 -72
- package/examples/structured-output.ts +0 -60
- package/examples/test-compact.ts +0 -65
- package/examples/test-rewind.ts +0 -87
- package/examples/tool-controls.ts +0 -37
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec mode: approve and implement in the same session.
|
|
3
|
+
*
|
|
4
|
+
* Approving the spec with `ProceedOnce` keeps implementation in the session
|
|
5
|
+
* that planned it. The example prints its session id and reports that no
|
|
6
|
+
* handoff occurred, which is what distinguishes its output from
|
|
7
|
+
* spec-mode-new-session.ts.
|
|
8
|
+
*
|
|
9
|
+
* A handoff would announce itself on the notification envelope: `params.sessionId`
|
|
10
|
+
* on incoming notifications would stop matching `session.id`. The typed message
|
|
11
|
+
* stream carries no session id per message, so the example subscribes with
|
|
12
|
+
* `session.onNotification()` and validates each envelope with
|
|
13
|
+
* `SessionNotificationSchema`.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* npx tsx examples/node/spec-mode-same-session.ts
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
20
|
+
import { tmpdir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
type ClientPermissionHandler,
|
|
25
|
+
DroidInteractionMode,
|
|
26
|
+
DroidMessageType,
|
|
27
|
+
ReasoningEffort,
|
|
28
|
+
SessionNotificationSchema,
|
|
29
|
+
ToolConfirmationOutcome,
|
|
30
|
+
ToolConfirmationType,
|
|
31
|
+
createSession,
|
|
32
|
+
} from '@factory/droid-sdk/node';
|
|
33
|
+
|
|
34
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'droid-sdk-spec-'));
|
|
35
|
+
const outputPath = join(tempDir, 'hello.txt');
|
|
36
|
+
|
|
37
|
+
let handoffSessionId: string | null = null;
|
|
38
|
+
|
|
39
|
+
const handlePermissionRequest: ClientPermissionHandler = ({
|
|
40
|
+
toolUses,
|
|
41
|
+
options,
|
|
42
|
+
}) => {
|
|
43
|
+
const exitsSpecMode =
|
|
44
|
+
toolUses.length > 0 &&
|
|
45
|
+
toolUses.every(
|
|
46
|
+
(toolUse) => toolUse.details.type === ToolConfirmationType.ExitSpecMode
|
|
47
|
+
);
|
|
48
|
+
const createsExpectedFile =
|
|
49
|
+
toolUses.length > 0 &&
|
|
50
|
+
toolUses.every(
|
|
51
|
+
(toolUse) =>
|
|
52
|
+
toolUse.details.type === ToolConfirmationType.Create &&
|
|
53
|
+
toolUse.details.filePath === outputPath
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const outcome =
|
|
57
|
+
exitsSpecMode || createsExpectedFile
|
|
58
|
+
? ToolConfirmationOutcome.ProceedOnce
|
|
59
|
+
: ToolConfirmationOutcome.Cancel;
|
|
60
|
+
if (!options.some((option) => option.value === outcome)) {
|
|
61
|
+
throw new Error(`Outcome not offered: ${outcome}`);
|
|
62
|
+
}
|
|
63
|
+
return outcome;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const session = await createSession({
|
|
68
|
+
interactionMode: DroidInteractionMode.Spec,
|
|
69
|
+
specModeReasoningEffort: ReasoningEffort.High,
|
|
70
|
+
permissionHandler: handlePermissionRequest,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// The typed message stream carries no per-message session id, and the
|
|
74
|
+
// precise handoff signal the SDK uses internally is not part of the public
|
|
75
|
+
// surface, so a foreign envelope session id is the available proxy. It is
|
|
76
|
+
// only a proxy: any notification scoped to another session also trips it.
|
|
77
|
+
const unsubscribe = session.onNotification((notification) => {
|
|
78
|
+
const parsed = SessionNotificationSchema.safeParse(notification);
|
|
79
|
+
if (!parsed.success) return;
|
|
80
|
+
const envelopeSessionId = parsed.data.params.sessionId;
|
|
81
|
+
if (envelopeSessionId !== undefined && envelopeSessionId !== session.id) {
|
|
82
|
+
handoffSessionId = envelopeSessionId;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
for await (const message of session.stream(
|
|
88
|
+
`Plan then create ${outputPath} containing "Hello from Droid".`
|
|
89
|
+
)) {
|
|
90
|
+
if (message.type === DroidMessageType.Result && !message.success) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
message.error?.message ?? `Turn ended: ${message.subtype}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} finally {
|
|
97
|
+
unsubscribe();
|
|
98
|
+
await session.close();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(`planning session: ${session.id}`);
|
|
102
|
+
console.log(
|
|
103
|
+
`implementing session: ${handoffSessionId ?? '<same session>'}`
|
|
104
|
+
);
|
|
105
|
+
console.log(await readFile(outputPath, 'utf8'));
|
|
106
|
+
} finally {
|
|
107
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// A foreign session id is only a proxy for a handoff, so warn rather than
|
|
111
|
+
// fail: it may mean `ProceedOnce` handed implementation off, or merely that
|
|
112
|
+
// some unrelated session-scoped notification arrived during the run.
|
|
113
|
+
if (handoffSessionId !== null) {
|
|
114
|
+
console.warn(
|
|
115
|
+
`Observed a notification scoped to a different session (${String(handoffSessionId)}). ` +
|
|
116
|
+
'That may mean implementation was handed off, or that unrelated ' +
|
|
117
|
+
'session-scoped activity occurred during this run.'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured output example.
|
|
3
|
+
*
|
|
4
|
+
* Passes a JSON schema via `outputFormat` and reads the parsed object back
|
|
5
|
+
* from `result.structuredOutput`.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx tsx examples/node/structured-output.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { OutputFormatType, run } from '@factory/droid-sdk/node';
|
|
12
|
+
|
|
13
|
+
interface CodeReview {
|
|
14
|
+
summary: string;
|
|
15
|
+
findings: Array<{
|
|
16
|
+
line: number;
|
|
17
|
+
severity: 'low' | 'medium' | 'high';
|
|
18
|
+
message: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isCodeReview(value: unknown): value is CodeReview {
|
|
23
|
+
if (
|
|
24
|
+
value === null ||
|
|
25
|
+
typeof value !== 'object' ||
|
|
26
|
+
!('summary' in value) ||
|
|
27
|
+
typeof value.summary !== 'string' ||
|
|
28
|
+
!('findings' in value) ||
|
|
29
|
+
!Array.isArray(value.findings)
|
|
30
|
+
) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return value.findings.every(
|
|
35
|
+
(finding) =>
|
|
36
|
+
finding !== null &&
|
|
37
|
+
typeof finding === 'object' &&
|
|
38
|
+
'line' in finding &&
|
|
39
|
+
typeof finding.line === 'number' &&
|
|
40
|
+
'severity' in finding &&
|
|
41
|
+
['low', 'medium', 'high'].includes(String(finding.severity)) &&
|
|
42
|
+
'message' in finding &&
|
|
43
|
+
typeof finding.message === 'string'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const result = await run(
|
|
48
|
+
`Review this code for correctness issues:
|
|
49
|
+
|
|
50
|
+
function getDisplayName(user) {
|
|
51
|
+
return user.profile.name.trim();
|
|
52
|
+
}`,
|
|
53
|
+
{
|
|
54
|
+
outputFormat: {
|
|
55
|
+
type: OutputFormatType.JsonSchema,
|
|
56
|
+
schema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
summary: { type: 'string' },
|
|
60
|
+
findings: {
|
|
61
|
+
type: 'array',
|
|
62
|
+
items: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
line: { type: 'number' },
|
|
66
|
+
severity: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
enum: ['low', 'medium', 'high'],
|
|
69
|
+
},
|
|
70
|
+
message: { type: 'string' },
|
|
71
|
+
},
|
|
72
|
+
required: ['line', 'severity', 'message'],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ['summary', 'findings'],
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (!result.success) {
|
|
84
|
+
const detail =
|
|
85
|
+
result.structuredOutputError?.message ??
|
|
86
|
+
result.error?.message ??
|
|
87
|
+
'Unknown error';
|
|
88
|
+
throw new Error(`run() failed (${result.subtype}): ${detail}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The SDK returns `unknown`; validate it before reading fields.
|
|
92
|
+
if (!isCodeReview(result.structuredOutput)) {
|
|
93
|
+
throw new Error('run() returned an unexpected structured output shape.');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(JSON.stringify(result.structuredOutput, null, 2));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool controls example.
|
|
3
|
+
*
|
|
4
|
+
* Disables tools at session creation, then inspects availability.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx tsx examples/node/tool-controls.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createSession } from '@factory/droid-sdk/node';
|
|
11
|
+
|
|
12
|
+
const session = await createSession({
|
|
13
|
+
disabledToolIds: ['Execute'],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const tools = await session.listTools();
|
|
18
|
+
|
|
19
|
+
for (const tool of tools) {
|
|
20
|
+
if (['Read', 'Grep', 'Execute'].includes(tool.id)) {
|
|
21
|
+
console.log(`${tool.id}: ${String(tool.allowed)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} finally {
|
|
25
|
+
await session.close();
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@factory/droid-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TypeScript SDK for the Factory Droid CLI",
|
|
6
6
|
"keywords": [
|
|
@@ -12,32 +12,34 @@
|
|
|
12
12
|
"sdk"
|
|
13
13
|
],
|
|
14
14
|
"license": "Apache-2.0",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"module": "./dist/index.js",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
18
17
|
"types": "./dist/index.d.ts",
|
|
19
18
|
"exports": {
|
|
20
19
|
".": {
|
|
21
20
|
"import": {
|
|
22
|
-
"types": "./dist/index.d.
|
|
23
|
-
"default": "./dist/index.
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
|
+
"default": "./dist/index.mjs"
|
|
24
23
|
},
|
|
25
24
|
"require": {
|
|
26
|
-
"types": "./dist/index.d.
|
|
27
|
-
"default": "./dist/index.
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
28
27
|
}
|
|
29
28
|
},
|
|
30
|
-
"./
|
|
29
|
+
"./node": {
|
|
31
30
|
"import": {
|
|
32
|
-
"types": "./dist/
|
|
33
|
-
"default": "./dist/
|
|
31
|
+
"types": "./dist/node.d.mts",
|
|
32
|
+
"default": "./dist/node.mjs"
|
|
34
33
|
},
|
|
35
34
|
"require": {
|
|
36
|
-
"types": "./dist/
|
|
37
|
-
"default": "./dist/
|
|
35
|
+
"types": "./dist/node.d.ts",
|
|
36
|
+
"default": "./dist/node.js"
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
},
|
|
40
|
+
"browser": {
|
|
41
|
+
"ws": false
|
|
42
|
+
},
|
|
41
43
|
"files": [
|
|
42
44
|
"dist",
|
|
43
45
|
"docs",
|
|
@@ -45,18 +47,6 @@
|
|
|
45
47
|
"README.md",
|
|
46
48
|
"LICENSE"
|
|
47
49
|
],
|
|
48
|
-
"scripts": {
|
|
49
|
-
"build": "tsup",
|
|
50
|
-
"prepack": "npm run build",
|
|
51
|
-
"typecheck": "tsc --noEmit",
|
|
52
|
-
"typecheck:examples": "tsc --noEmit -p tsconfig.examples.json",
|
|
53
|
-
"test": "vitest run",
|
|
54
|
-
"test:coverage": "vitest run --coverage",
|
|
55
|
-
"lint": "eslint src/ tests/ examples/",
|
|
56
|
-
"fix": "eslint --fix src/ tests/ examples/",
|
|
57
|
-
"format": "prettier --write .",
|
|
58
|
-
"format:check": "prettier --check ."
|
|
59
|
-
},
|
|
60
50
|
"repository": {
|
|
61
51
|
"type": "git",
|
|
62
52
|
"url": "git+https://github.com/Factory-AI/droid-sdk-typescript.git"
|
|
@@ -66,26 +56,14 @@
|
|
|
66
56
|
},
|
|
67
57
|
"dependencies": {
|
|
68
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
69
|
-
"
|
|
59
|
+
"@opentelemetry/api": "^1.9.0",
|
|
60
|
+
"eventemitter3": "^5.0.1",
|
|
61
|
+
"fast-deep-equal": "^3.1.3",
|
|
62
|
+
"isows": "^1.0.7",
|
|
63
|
+
"uuid": "^9.0.0",
|
|
70
64
|
"ws": "^8.21.0",
|
|
71
65
|
"zod": "^3.24.0"
|
|
72
66
|
},
|
|
73
|
-
"devDependencies": {
|
|
74
|
-
"@eslint/js": "^9.39.4",
|
|
75
|
-
"@types/node": "^22.15.0",
|
|
76
|
-
"@types/uuid": "^10.0.0",
|
|
77
|
-
"@types/ws": "^8.18.1",
|
|
78
|
-
"@vitest/coverage-v8": "^3.1.0",
|
|
79
|
-
"eslint": "^9.24.0",
|
|
80
|
-
"eslint-import-resolver-typescript": "^4.4.4",
|
|
81
|
-
"eslint-plugin-import": "^2.32.0",
|
|
82
|
-
"eslint-plugin-unused-imports": "^4.4.1",
|
|
83
|
-
"prettier": "^3.8.1",
|
|
84
|
-
"tsup": "^8.4.0",
|
|
85
|
-
"typescript": "^5.8.0",
|
|
86
|
-
"typescript-eslint": "^8.57.2",
|
|
87
|
-
"vitest": "^3.1.0"
|
|
88
|
-
},
|
|
89
67
|
"homepage": "https://github.com/Factory-AI/droid-sdk-typescript#readme",
|
|
90
68
|
"bugs": {
|
|
91
69
|
"url": "https://github.com/Factory-AI/droid-sdk-typescript/issues"
|