deepagentsdk 0.11.1 → 0.13.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.
Files changed (99) hide show
  1. package/dist/adapters/elements/index.cjs +274 -0
  2. package/dist/adapters/elements/index.cjs.map +1 -0
  3. package/dist/adapters/elements/index.d.cts +122 -0
  4. package/dist/adapters/elements/index.d.mts +122 -0
  5. package/dist/adapters/elements/index.mjs +268 -0
  6. package/dist/adapters/elements/index.mjs.map +1 -0
  7. package/dist/agent-BDM-PIu8.d.mts +1500 -0
  8. package/dist/agent-DToEVxs-.d.cts +1500 -0
  9. package/dist/chunk-C5azi7Hr.cjs +67 -0
  10. package/dist/cli/index.cjs +3162 -0
  11. package/dist/cli/index.cjs.map +1 -0
  12. package/dist/cli/index.d.cts +1 -0
  13. package/dist/cli/index.d.mts +1 -0
  14. package/dist/cli/index.mjs +3120 -0
  15. package/dist/cli/index.mjs.map +1 -0
  16. package/dist/file-saver-BYPKakT4.cjs +3990 -0
  17. package/dist/file-saver-BYPKakT4.cjs.map +1 -0
  18. package/dist/file-saver-Hj5so3dV.mjs +3568 -0
  19. package/dist/file-saver-Hj5so3dV.mjs.map +1 -0
  20. package/dist/index.cjs +1481 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +1233 -0
  23. package/dist/index.d.mts +1233 -0
  24. package/dist/index.mjs +1381 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/load-BBYEnMwz.mjs +142 -0
  27. package/dist/load-BBYEnMwz.mjs.map +1 -0
  28. package/dist/load-BDxe6Cet.mjs +3 -0
  29. package/dist/load-BrRAKlO6.cjs +163 -0
  30. package/dist/load-BrRAKlO6.cjs.map +1 -0
  31. package/dist/load-DqllBbDc.cjs +4 -0
  32. package/package.json +26 -12
  33. package/src/adapters/elements/index.ts +0 -27
  34. package/src/adapters/elements/messageAdapter.ts +0 -165
  35. package/src/adapters/elements/statusAdapter.ts +0 -39
  36. package/src/adapters/elements/types.ts +0 -97
  37. package/src/adapters/elements/useElementsAdapter.ts +0 -261
  38. package/src/agent.ts +0 -1258
  39. package/src/backends/composite.ts +0 -273
  40. package/src/backends/filesystem.ts +0 -692
  41. package/src/backends/index.ts +0 -22
  42. package/src/backends/local-sandbox.ts +0 -175
  43. package/src/backends/persistent.ts +0 -593
  44. package/src/backends/sandbox.ts +0 -510
  45. package/src/backends/state.ts +0 -244
  46. package/src/backends/utils.ts +0 -287
  47. package/src/checkpointer/file-saver.ts +0 -98
  48. package/src/checkpointer/index.ts +0 -5
  49. package/src/checkpointer/kv-saver.ts +0 -82
  50. package/src/checkpointer/memory-saver.ts +0 -82
  51. package/src/checkpointer/types.ts +0 -125
  52. package/src/cli/components/ApiKeyInput.tsx +0 -300
  53. package/src/cli/components/FilePreview.tsx +0 -237
  54. package/src/cli/components/Input.tsx +0 -277
  55. package/src/cli/components/Message.tsx +0 -93
  56. package/src/cli/components/ModelSelection.tsx +0 -338
  57. package/src/cli/components/SlashMenu.tsx +0 -101
  58. package/src/cli/components/StatusBar.tsx +0 -89
  59. package/src/cli/components/Subagent.tsx +0 -91
  60. package/src/cli/components/TodoList.tsx +0 -133
  61. package/src/cli/components/ToolApproval.tsx +0 -70
  62. package/src/cli/components/ToolCall.tsx +0 -144
  63. package/src/cli/components/ToolCallSummary.tsx +0 -175
  64. package/src/cli/components/Welcome.tsx +0 -75
  65. package/src/cli/components/index.ts +0 -24
  66. package/src/cli/hooks/index.ts +0 -12
  67. package/src/cli/hooks/useAgent.ts +0 -933
  68. package/src/cli/index.tsx +0 -1066
  69. package/src/cli/theme.ts +0 -205
  70. package/src/cli/utils/model-list.ts +0 -365
  71. package/src/constants/errors.ts +0 -29
  72. package/src/constants/limits.ts +0 -195
  73. package/src/index.ts +0 -176
  74. package/src/middleware/agent-memory.ts +0 -330
  75. package/src/prompts.ts +0 -196
  76. package/src/skills/index.ts +0 -2
  77. package/src/skills/load.ts +0 -191
  78. package/src/skills/types.ts +0 -53
  79. package/src/tools/execute.ts +0 -167
  80. package/src/tools/filesystem.ts +0 -418
  81. package/src/tools/index.ts +0 -39
  82. package/src/tools/subagent.ts +0 -443
  83. package/src/tools/todos.ts +0 -101
  84. package/src/tools/web.ts +0 -567
  85. package/src/types/backend.ts +0 -177
  86. package/src/types/core.ts +0 -220
  87. package/src/types/events.ts +0 -430
  88. package/src/types/index.ts +0 -94
  89. package/src/types/structured-output.ts +0 -43
  90. package/src/types/subagent.ts +0 -96
  91. package/src/types.ts +0 -22
  92. package/src/utils/approval.ts +0 -213
  93. package/src/utils/events.ts +0 -416
  94. package/src/utils/eviction.ts +0 -181
  95. package/src/utils/index.ts +0 -34
  96. package/src/utils/model-parser.ts +0 -38
  97. package/src/utils/patch-tool-calls.ts +0 -233
  98. package/src/utils/project-detection.ts +0 -32
  99. package/src/utils/summarization.ts +0 -254
@@ -1,338 +0,0 @@
1
- /**
2
- * Model Selection Panel - Interactive model selection with arrow keys.
3
- */
4
-
5
- import React, { useState, useEffect, useMemo } from "react";
6
- import { Box, Text, useInput } from "ink";
7
- import { Spinner } from "@inkjs/ui";
8
- import { colors, emoji } from "../theme";
9
- import {
10
- getModelsByProvider,
11
- detectAvailableProviders,
12
- type AvailableModel,
13
- } from "../utils/model-list";
14
-
15
- interface ModelSelectionPanelProps {
16
- currentModel?: string;
17
- /** Callback when a model is selected */
18
- onModelSelect?: (modelId: string) => void;
19
- /** Callback to close the panel */
20
- onClose?: () => void;
21
- }
22
-
23
- interface LoadingState {
24
- loading: boolean;
25
- anthropicModels: AvailableModel[];
26
- openaiModels: AvailableModel[];
27
- errors: { provider: string; error: string }[];
28
- }
29
-
30
- export function ModelSelectionPanel({
31
- currentModel,
32
- onModelSelect,
33
- onClose,
34
- }: ModelSelectionPanelProps): React.ReactElement {
35
- const providers = detectAvailableProviders();
36
- const hasAnyKey = providers.anthropic || providers.openai;
37
-
38
- const [state, setState] = useState<LoadingState>({
39
- loading: true,
40
- anthropicModels: [],
41
- openaiModels: [],
42
- errors: [],
43
- });
44
-
45
- const [selectedIndex, setSelectedIndex] = useState(0);
46
-
47
- // Combine all models into a flat list for navigation
48
- const allModels = useMemo(() => {
49
- const models: AvailableModel[] = [];
50
- if (state.anthropicModels.length > 0) {
51
- models.push(...state.anthropicModels);
52
- }
53
- if (state.openaiModels.length > 0) {
54
- models.push(...state.openaiModels);
55
- }
56
- return models;
57
- }, [state.anthropicModels, state.openaiModels]);
58
-
59
- // Find current model index to set initial selection
60
- useEffect(() => {
61
- if (allModels.length > 0 && currentModel) {
62
- const currentIndex = allModels.findIndex((m) => isCurrentModel(currentModel, m));
63
- if (currentIndex >= 0) {
64
- setSelectedIndex(currentIndex);
65
- }
66
- }
67
- }, [allModels, currentModel]);
68
-
69
- // Handle keyboard input
70
- useInput((input, key) => {
71
- if (state.loading) return;
72
-
73
- if (key.upArrow) {
74
- setSelectedIndex((prev) => (prev > 0 ? prev - 1 : allModels.length - 1));
75
- } else if (key.downArrow) {
76
- setSelectedIndex((prev) => (prev < allModels.length - 1 ? prev + 1 : 0));
77
- } else if (key.return) {
78
- const selectedModel = allModels[selectedIndex];
79
- if (selectedModel) {
80
- onModelSelect?.(selectedModel.id);
81
- onClose?.();
82
- }
83
- } else if (key.escape) {
84
- onClose?.();
85
- }
86
- });
87
-
88
- // Fetch models on mount
89
- useEffect(() => {
90
- if (!hasAnyKey) {
91
- setState({ loading: false, anthropicModels: [], openaiModels: [], errors: [] });
92
- return;
93
- }
94
-
95
- let cancelled = false;
96
-
97
- async function loadModels() {
98
- try {
99
- const result = await getModelsByProvider();
100
- if (!cancelled) {
101
- setState({
102
- loading: false,
103
- anthropicModels: result.anthropic || [],
104
- openaiModels: result.openai || [],
105
- errors: result.errors,
106
- });
107
- }
108
- } catch (error) {
109
- if (!cancelled) {
110
- setState({
111
- loading: false,
112
- anthropicModels: [],
113
- openaiModels: [],
114
- errors: [{ provider: "Unknown", error: String(error) }],
115
- });
116
- }
117
- }
118
- }
119
-
120
- loadModels();
121
-
122
- return () => {
123
- cancelled = true;
124
- };
125
- }, [hasAnyKey]);
126
-
127
- // No API keys configured
128
- if (!hasAnyKey) {
129
- return (
130
- <Box
131
- flexDirection="column"
132
- borderStyle="single"
133
- borderColor={colors.warning}
134
- paddingX={2}
135
- paddingY={1}
136
- marginY={1}
137
- >
138
- <Text bold color={colors.warning}>
139
- ⚠️ No API Keys Found
140
- </Text>
141
- <Box height={1} />
142
- <Text>Add an API key first to see available models.</Text>
143
- <Box height={1} />
144
- <Text color={colors.primary}>Run /apikey to add your API key</Text>
145
- <Box height={1} />
146
- <Text dimColor>Supported providers:</Text>
147
- <Text dimColor> • Anthropic (Claude)</Text>
148
- <Text dimColor> • OpenAI (GPT)</Text>
149
- </Box>
150
- );
151
- }
152
-
153
- // Loading state
154
- if (state.loading) {
155
- return (
156
- <Box
157
- flexDirection="column"
158
- borderStyle="single"
159
- borderColor={colors.muted}
160
- paddingX={2}
161
- paddingY={1}
162
- marginY={1}
163
- >
164
- <Text bold color={colors.info}>
165
- {emoji.model} Select Model
166
- </Text>
167
- <Box height={1} />
168
- <Box>
169
- <Spinner label="Fetching models from API..." />
170
- </Box>
171
- </Box>
172
- );
173
- }
174
-
175
- // Check if we have any models
176
- const hasModels = allModels.length > 0;
177
-
178
- // No models found (API errors)
179
- if (!hasModels && state.errors.length > 0) {
180
- return (
181
- <Box
182
- flexDirection="column"
183
- borderStyle="single"
184
- borderColor={colors.error}
185
- paddingX={2}
186
- paddingY={1}
187
- marginY={1}
188
- >
189
- <Text bold color={colors.error}>
190
- {emoji.error} Failed to Fetch Models
191
- </Text>
192
- <Box height={1} />
193
- {state.errors.map((err, i) => (
194
- <Text key={i} color={colors.error}>
195
- {err.provider}: {err.error}
196
- </Text>
197
- ))}
198
- <Box height={1} />
199
- <Text dimColor>Check your API key and try again with /apikey</Text>
200
- </Box>
201
- );
202
- }
203
-
204
- // Calculate the offset for each provider section
205
- let anthropicOffset = 0;
206
- let openaiOffset = state.anthropicModels.length;
207
-
208
- return (
209
- <Box
210
- flexDirection="column"
211
- borderStyle="single"
212
- borderColor={colors.primary}
213
- paddingX={2}
214
- paddingY={1}
215
- marginY={1}
216
- >
217
- <Text bold color={colors.info}>
218
- {emoji.model} Select Model
219
- </Text>
220
- <Box height={1} />
221
-
222
- {/* Show any errors */}
223
- {state.errors.length > 0 && (
224
- <>
225
- {state.errors.map((err, i) => (
226
- <Text key={i} color={colors.warning}>
227
- {emoji.warning} {err.provider}: {err.error}
228
- </Text>
229
- ))}
230
- <Box height={1} />
231
- </>
232
- )}
233
-
234
- {/* Anthropic Models */}
235
- {state.anthropicModels.length > 0 && (
236
- <>
237
- <Text bold color={colors.primary}>
238
- Anthropic Claude
239
- </Text>
240
- {state.anthropicModels.map((model, index) => {
241
- const globalIndex = anthropicOffset + index;
242
- const isSelected = globalIndex === selectedIndex;
243
- const isCurrent = isCurrentModel(currentModel, model);
244
- return (
245
- <ModelItem
246
- key={model.id}
247
- model={model}
248
- isSelected={isSelected}
249
- isCurrent={isCurrent}
250
- />
251
- );
252
- })}
253
- <Box height={1} />
254
- </>
255
- )}
256
-
257
- {/* OpenAI Models */}
258
- {state.openaiModels.length > 0 && (
259
- <>
260
- <Text bold color={colors.primary}>
261
- OpenAI GPT
262
- </Text>
263
- {state.openaiModels.map((model, index) => {
264
- const globalIndex = openaiOffset + index;
265
- const isSelected = globalIndex === selectedIndex;
266
- const isCurrent = isCurrentModel(currentModel, model);
267
- return (
268
- <ModelItem
269
- key={model.id}
270
- model={model}
271
- isSelected={isSelected}
272
- isCurrent={isCurrent}
273
- />
274
- );
275
- })}
276
- <Box height={1} />
277
- </>
278
- )}
279
-
280
- {/* Navigation hint */}
281
- <Text dimColor>↑/↓ Navigate • Enter Select • Esc Cancel</Text>
282
- </Box>
283
- );
284
- }
285
-
286
- /**
287
- * Check if a model matches the current model.
288
- */
289
- function isCurrentModel(currentModel: string | undefined, model: AvailableModel): boolean {
290
- if (!currentModel) return false;
291
- return (
292
- currentModel === model.id ||
293
- currentModel === model.name ||
294
- currentModel === `${model.provider}/${model.name}` ||
295
- (currentModel.startsWith(`${model.provider}/`) && currentModel === model.id)
296
- );
297
- }
298
-
299
- interface ModelItemProps {
300
- model: AvailableModel;
301
- isSelected: boolean;
302
- isCurrent: boolean;
303
- }
304
-
305
- function ModelItem({ model, isSelected, isCurrent }: ModelItemProps): React.ReactElement {
306
- // Determine the indicator
307
- let indicator = " ";
308
- let textColor: string | undefined = undefined;
309
- let isBold = false;
310
-
311
- if (isSelected) {
312
- indicator = "▸ ";
313
- textColor = colors.primary;
314
- isBold = true;
315
- }
316
-
317
- if (isCurrent) {
318
- indicator = isSelected ? "▸✓" : " ✓";
319
- textColor = isSelected ? colors.primary : colors.success;
320
- }
321
-
322
- return (
323
- <Box marginLeft={1}>
324
- <Text color={isSelected ? colors.primary : isCurrent ? colors.success : undefined}>
325
- {indicator}
326
- </Text>
327
- <Text color={textColor} bold={isBold}>
328
- {model.id}
329
- </Text>
330
- {model.description && (
331
- <>
332
- <Text dimColor> - </Text>
333
- <Text dimColor>{model.description}</Text>
334
- </>
335
- )}
336
- </Box>
337
- );
338
- }
@@ -1,101 +0,0 @@
1
- /**
2
- * Slash command autocomplete menu component.
3
- */
4
- import React from "react";
5
- import { Box, Text } from "ink";
6
- import { colors, filterCommands, type SlashCommand } from "../theme";
7
-
8
- interface SlashMenuProps {
9
- /** Current input value to filter commands */
10
- filter: string;
11
- /** Maximum number of items to show */
12
- maxItems?: number;
13
- }
14
-
15
- export function SlashMenu({
16
- filter,
17
- maxItems = 8,
18
- }: SlashMenuProps): React.ReactElement | null {
19
- // Only show menu when input starts with /
20
- if (!filter.startsWith("/")) {
21
- return null;
22
- }
23
-
24
- const filtered = filterCommands(filter);
25
-
26
- if (filtered.length === 0) {
27
- return (
28
- <Box paddingLeft={2} marginTop={1}>
29
- <Text dimColor>No matching commands</Text>
30
- </Box>
31
- );
32
- }
33
-
34
- const displayItems = filtered.slice(0, maxItems);
35
- const hasMore = filtered.length > maxItems;
36
-
37
- return (
38
- <Box flexDirection="column" marginTop={1} paddingLeft={2}>
39
- {displayItems.map((cmd) => (
40
- <SlashMenuItem key={cmd.command} command={cmd} />
41
- ))}
42
- {hasMore && (
43
- <Text dimColor>... and {filtered.length - maxItems} more</Text>
44
- )}
45
- </Box>
46
- );
47
- }
48
-
49
- interface SlashMenuItemProps {
50
- command: SlashCommand;
51
- }
52
-
53
- function SlashMenuItem({ command }: SlashMenuItemProps): React.ReactElement {
54
- const aliases =
55
- command.aliases.length > 0 ? ` (${command.aliases.join(", ")})` : "";
56
-
57
- return (
58
- <Box>
59
- <Text color={colors.info}>{command.command}</Text>
60
- <Text dimColor>{aliases}</Text>
61
- <Text dimColor> - {command.description}</Text>
62
- </Box>
63
- );
64
- }
65
-
66
- /**
67
- * Full slash menu panel for /help command.
68
- */
69
- export function SlashMenuPanel(): React.ReactElement {
70
- const commands = filterCommands();
71
-
72
- return (
73
- <Box
74
- flexDirection="column"
75
- borderStyle="single"
76
- borderColor={colors.muted}
77
- paddingX={2}
78
- paddingY={1}
79
- marginY={1}
80
- >
81
- <Text bold color={colors.info}>
82
- Available Commands
83
- </Text>
84
- <Box height={1} />
85
- {commands.map((cmd) => (
86
- <Box key={cmd.command} flexDirection="column" marginBottom={1}>
87
- <Box>
88
- <Text color={colors.info}>{cmd.command}</Text>
89
- {cmd.aliases.length > 0 && (
90
- <Text dimColor> ({cmd.aliases.join(", ")})</Text>
91
- )}
92
- </Box>
93
- <Box paddingLeft={2}>
94
- <Text dimColor>{cmd.description}</Text>
95
- </Box>
96
- </Box>
97
- ))}
98
- </Box>
99
- );
100
- }
101
-
@@ -1,89 +0,0 @@
1
- /**
2
- * Compact status bar component.
3
- * Clean, minimal design inspired by Claude Code and OpenAI Codex.
4
- */
5
- import React from "react";
6
- import { Box, Text } from "ink";
7
- import { colors } from "../theme";
8
-
9
- interface FeatureFlags {
10
- promptCaching: boolean;
11
- eviction: boolean;
12
- summarization: boolean;
13
- }
14
-
15
- interface StatusBarProps {
16
- /** Current working directory */
17
- workDir: string;
18
- /** Current model name */
19
- model: string;
20
- /** Optional status indicator (idle, generating, etc.) */
21
- status?: "idle" | "thinking" | "streaming" | "tool-call" | "subagent" | "done" | "error";
22
- /** Feature flags to display */
23
- features?: FeatureFlags;
24
- /** Whether auto-approve mode is enabled */
25
- autoApproveEnabled?: boolean;
26
- /** Current session ID if checkpointing is enabled */
27
- sessionId?: string;
28
- }
29
-
30
- export function StatusBar({
31
- workDir,
32
- model,
33
- status = "idle",
34
- features,
35
- autoApproveEnabled = false,
36
- sessionId,
37
- }: StatusBarProps): React.ReactElement {
38
- // Get short model name
39
- const shortModel = model.split("/").pop() || model;
40
-
41
- // Status indicator - minimal
42
- const getStatusDisplay = () => {
43
- switch (status) {
44
- case "thinking":
45
- return <Text color={colors.warning}>●</Text>;
46
- case "streaming":
47
- return <Text color={colors.success}>●</Text>;
48
- case "tool-call":
49
- return <Text color={colors.tool}>●</Text>;
50
- case "subagent":
51
- return <Text color={colors.secondary}>●</Text>;
52
- case "error":
53
- return <Text color={colors.error}>●</Text>;
54
- case "done":
55
- return <Text color={colors.success}>●</Text>;
56
- default:
57
- return <Text dimColor>○</Text>;
58
- }
59
- };
60
-
61
- // Feature badges - compact
62
- const featureBadges: string[] = [];
63
- if (features?.promptCaching) featureBadges.push("⚡");
64
- if (features?.eviction) featureBadges.push("📦");
65
- if (features?.summarization) featureBadges.push("📝");
66
-
67
- return (
68
- <Box marginTop={1}>
69
- <Text dimColor>
70
- {getStatusDisplay()} {shortModel}
71
- {featureBadges.length > 0 && ` ${featureBadges.join(" ")}`}
72
- {" · "}
73
- {autoApproveEnabled ? (
74
- <Text color={colors.success}>🟢 Auto-approve</Text>
75
- ) : (
76
- <Text color={colors.warning}>🔴 Safe mode</Text>
77
- )}
78
- {sessionId && (
79
- <>
80
- {" · "}
81
- <Text dimColor>Session: {sessionId}</Text>
82
- </>
83
- )}
84
- {" · "}? for shortcuts
85
- </Text>
86
- </Box>
87
- );
88
- }
89
-
@@ -1,91 +0,0 @@
1
- /**
2
- * Subagent status display components.
3
- */
4
- import React from "react";
5
- import { Box, Text } from "ink";
6
- import { Spinner, StatusMessage } from "@inkjs/ui";
7
- import { emoji, colors } from "../theme";
8
-
9
- interface SubagentStartProps {
10
- /** Subagent name */
11
- name: string;
12
- /** Task description */
13
- task: string;
14
- /** Maximum task length to display */
15
- maxTaskLength?: number;
16
- }
17
-
18
- export function SubagentStart({
19
- name,
20
- task,
21
- maxTaskLength = 60,
22
- }: SubagentStartProps): React.ReactElement {
23
- const shortTask =
24
- task.length > maxTaskLength
25
- ? task.substring(0, maxTaskLength) + "..."
26
- : task;
27
-
28
- return (
29
- <Box flexDirection="column" marginY={1}>
30
- <Box>
31
- <Spinner
32
- label={`${emoji.subagent} Starting subagent: ${name}`}
33
- />
34
- </Box>
35
- <Box paddingLeft={4}>
36
- <Text dimColor>└─ {shortTask}</Text>
37
- </Box>
38
- </Box>
39
- );
40
- }
41
-
42
- interface SubagentFinishProps {
43
- /** Subagent name */
44
- name: string;
45
- }
46
-
47
- export function SubagentFinish({
48
- name,
49
- }: SubagentFinishProps): React.ReactElement {
50
- return (
51
- <Box marginY={1}>
52
- <StatusMessage variant="success">
53
- {emoji.subagent} Subagent {name} completed
54
- </StatusMessage>
55
- </Box>
56
- );
57
- }
58
-
59
- /**
60
- * Subagent running indicator (for when a subagent is actively working).
61
- */
62
- interface SubagentRunningProps {
63
- name: string;
64
- task: string;
65
- }
66
-
67
- export function SubagentRunning({
68
- name,
69
- task,
70
- }: SubagentRunningProps): React.ReactElement {
71
- const shortTask = task.length > 50 ? task.substring(0, 50) + "..." : task;
72
-
73
- return (
74
- <Box
75
- flexDirection="column"
76
- borderStyle="single"
77
- borderColor={colors.secondary}
78
- paddingX={2}
79
- paddingY={1}
80
- marginY={1}
81
- >
82
- <Box>
83
- <Spinner label={`${emoji.subagent} ${name}`} />
84
- </Box>
85
- <Box paddingLeft={2}>
86
- <Text dimColor>{shortTask}</Text>
87
- </Box>
88
- </Box>
89
- );
90
- }
91
-