@oceanum/oceanumlab 4.6.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 (83) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +116 -0
  3. package/lib/DatameshUI.d.ts +5 -0
  4. package/lib/DatameshUI.js +8 -0
  5. package/lib/DatameshUI.js.map +1 -0
  6. package/lib/DatameshWidget.d.ts +59 -0
  7. package/lib/DatameshWidget.js +573 -0
  8. package/lib/DatameshWidget.js.map +1 -0
  9. package/lib/DatasourceItem.d.ts +12 -0
  10. package/lib/DatasourceItem.js +47 -0
  11. package/lib/DatasourceItem.js.map +1 -0
  12. package/lib/aiLoop.d.ts +60 -0
  13. package/lib/aiLoop.js +85 -0
  14. package/lib/aiLoop.js.map +1 -0
  15. package/lib/chatRouter.d.ts +64 -0
  16. package/lib/chatRouter.js +225 -0
  17. package/lib/chatRouter.js.map +1 -0
  18. package/lib/constants.d.ts +14 -0
  19. package/lib/constants.js +15 -0
  20. package/lib/constants.js.map +1 -0
  21. package/lib/conversationPin.d.ts +68 -0
  22. package/lib/conversationPin.js +120 -0
  23. package/lib/conversationPin.js.map +1 -0
  24. package/lib/handler.d.ts +8 -0
  25. package/lib/handler.js +36 -0
  26. package/lib/handler.js.map +1 -0
  27. package/lib/index.d.ts +16 -0
  28. package/lib/index.js +273 -0
  29. package/lib/index.js.map +1 -0
  30. package/lib/kernelHandoff.d.ts +61 -0
  31. package/lib/kernelHandoff.js +236 -0
  32. package/lib/kernelHandoff.js.map +1 -0
  33. package/lib/notebookContext.d.ts +48 -0
  34. package/lib/notebookContext.js +118 -0
  35. package/lib/notebookContext.js.map +1 -0
  36. package/lib/notebookHost.d.ts +6 -0
  37. package/lib/notebookHost.js +72 -0
  38. package/lib/notebookHost.js.map +1 -0
  39. package/lib/notebookRun.d.ts +37 -0
  40. package/lib/notebookRun.js +70 -0
  41. package/lib/notebookRun.js.map +1 -0
  42. package/lib/progress.d.ts +43 -0
  43. package/lib/progress.js +103 -0
  44. package/lib/progress.js.map +1 -0
  45. package/lib/sse.d.ts +26 -0
  46. package/lib/sse.js +112 -0
  47. package/lib/sse.js.map +1 -0
  48. package/package.json +151 -0
  49. package/schema/datamesh-connect.json +35 -0
  50. package/src/DatameshUI.tsx +15 -0
  51. package/src/DatameshWidget.tsx +909 -0
  52. package/src/DatasourceItem.tsx +124 -0
  53. package/src/__tests__/aiLoop.spec.ts +304 -0
  54. package/src/__tests__/chatRouter.spec.ts +315 -0
  55. package/src/__tests__/conversationPin.spec.ts +236 -0
  56. package/src/__tests__/kernelHandoff.spec.ts +106 -0
  57. package/src/__tests__/notebookContext.spec.ts +97 -0
  58. package/src/__tests__/notebookHost.spec.ts +195 -0
  59. package/src/__tests__/notebookRun.spec.ts +95 -0
  60. package/src/__tests__/notebookSnapshot.spec.ts +62 -0
  61. package/src/__tests__/oceanumlab.spec.ts +9 -0
  62. package/src/__tests__/sse.spec.ts +304 -0
  63. package/src/aiLoop.ts +140 -0
  64. package/src/chatRouter.ts +329 -0
  65. package/src/constants.ts +16 -0
  66. package/src/conversationPin.ts +148 -0
  67. package/src/global.d.ts +5 -0
  68. package/src/handler.ts +46 -0
  69. package/src/index.ts +327 -0
  70. package/src/kernelHandoff.ts +284 -0
  71. package/src/notebookContext.ts +144 -0
  72. package/src/notebookHost.ts +97 -0
  73. package/src/notebookRun.ts +104 -0
  74. package/src/progress.ts +121 -0
  75. package/src/sse.ts +127 -0
  76. package/src/svg.d.ts +4 -0
  77. package/style/base.css +5 -0
  78. package/style/icons/oceanum-dark.svg +1 -0
  79. package/style/icons/oceanum-light.svg +1 -0
  80. package/style/icons/oceanum.png +0 -0
  81. package/style/icons/oceanum.svg +1 -0
  82. package/style/index.css +477 -0
  83. package/style/index.js +1 -0
@@ -0,0 +1,47 @@
1
+ import React, { useState } from 'react';
2
+ import { caretDownIcon, caretRightIcon } from '@jupyterlab/ui-components';
3
+ import { ArrowBendDownRight } from '@phosphor-icons/react';
4
+ import '../style/index.css';
5
+ /**
6
+ * A React component for expandable containers.
7
+ */
8
+ export const DatasourceItem = ({ datasource, insertDatasource, onMouseDown }) => {
9
+ const [expanded, setExpandedValue] = useState(false);
10
+ const [isDragging, setIsDragging] = useState(false);
11
+ const handleToggleExpand = () => {
12
+ setExpandedValue(!expanded);
13
+ };
14
+ const startDrag = (e) => {
15
+ setIsDragging(true);
16
+ onMouseDown(e, datasource);
17
+ };
18
+ return (React.createElement("div", { className: expanded ? 'expanded' : '' },
19
+ React.createElement("div", { key: datasource.id, className: "datasource-item-title" },
20
+ React.createElement("button", { title: expanded ? 'Hide Details' : 'Show Details', onClick: handleToggleExpand, className: "datasource-item-expand" }, expanded ? (React.createElement(caretDownIcon.react, { tag: "span", elementPosition: "center", width: "20px" })) : (React.createElement(caretRightIcon.react, { tag: "span", elementPosition: "center", width: "20px" }))),
21
+ React.createElement("span", { title: datasource.description, className: isDragging
22
+ ? 'datasource-item-name'
23
+ : 'datasource-item-name draggable', onClick: handleToggleExpand, onMouseDown: startDrag, onMouseUp: () => setIsDragging(false) }, datasource.description),
24
+ React.createElement("button", { title: "Insert code to load datasource", onClick: handleToggleExpand, className: "datasource-item-action" },
25
+ React.createElement(ArrowBendDownRight, { size: 18, onClick: e => insertDatasource(datasource) }))),
26
+ expanded && (React.createElement("div", { className: 'datasource-item-details' },
27
+ React.createElement("div", { className: "datasource-query-field" },
28
+ "Datasource id: ",
29
+ React.createElement("span", null, datasource.datasource)),
30
+ datasource.geofilter && (React.createElement("div", { className: "datasource-query-field" },
31
+ "Geofilter type:",
32
+ React.createElement("span", null, datasource.geofilter.type))),
33
+ datasource.variables && (React.createElement("div", { className: "datasource-query-field" },
34
+ "Variables:",
35
+ React.createElement("span", null, datasource.variables.join(',')))),
36
+ datasource.timefilter && (React.createElement("div", { className: "datasource-query-field" },
37
+ "Timefilter:",
38
+ React.createElement("span", null, datasource.timefilter.times[0]),
39
+ React.createElement("span", null, "to"),
40
+ React.createElement("span", null, datasource.timefilter.times[1]))),
41
+ datasource.spatialref && (React.createElement("div", { className: "datasource-query-field" },
42
+ "Spatial ref:",
43
+ React.createElement("span", null, datasource.spatialref))),
44
+ React.createElement("div", null,
45
+ React.createElement("a", { href: `https://oceanum.io/datasets/${datasource.datasource}`, target: "_blank" }, "Datasource details"))))));
46
+ };
47
+ //# sourceMappingURL=DatasourceItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatasourceItem.js","sourceRoot":"","sources":["../src/DatasourceItem.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { ChatMessage, OceanumResponse } from './chatRouter';
2
+ import type { ObservedRun } from './notebookRun';
3
+ /** What `inject` reports back: the chat text, and what ran if anything did. */
4
+ export interface PlacedResponse {
5
+ message: string;
6
+ runs: ObservedRun[];
7
+ /**
8
+ * A code cell could not be executed at all (no kernel, pending input, the
9
+ * notebook closed, Stop). Its run says so; observing it would only bring
10
+ * back more code that cannot run either.
11
+ */
12
+ halted?: boolean;
13
+ }
14
+ /** The pieces the loop needs, injected so it can be tested without a notebook. */
15
+ export interface LoopDeps {
16
+ route(prompt: string, history: ChatMessage[], signal: AbortSignal): Promise<{
17
+ response: OceanumResponse;
18
+ hasCodeCellSelected: boolean;
19
+ }>;
20
+ observe(prompt: string, history: ChatMessage[], runs: ObservedRun[], signal: AbortSignal): Promise<OceanumResponse>;
21
+ place(response: OceanumResponse, options: {
22
+ replaceCodeCell: boolean;
23
+ autoRun: boolean;
24
+ signal: AbortSignal;
25
+ }): Promise<PlacedResponse>;
26
+ }
27
+ export interface LoopOptions {
28
+ /** Run code cells as they are placed. Workflow 1; required for 2. */
29
+ autoRunCode: boolean;
30
+ /** Send each run's output back and place what comes next. Workflow 2. */
31
+ iterate: boolean;
32
+ /** Safety net on observe requests; see `runChatLoop`. */
33
+ maxRounds: number;
34
+ signal: AbortSignal;
35
+ }
36
+ export declare const STOPPED = "Stopped.";
37
+ /**
38
+ * One prompt, start to finish, under the two switches.
39
+ *
40
+ * The loop lives here rather than on the server because the CLIENT owns the
41
+ * kernel: only it can run a cell and read what came back. The server sees one
42
+ * turn at a time through `/api/chat/observe`.
43
+ *
44
+ * autoRunCode off -> place blocks, stop. (workflow 3)
45
+ * autoRunCode on, iterate off -> place, run, stop. (workflow 1)
46
+ * autoRunCode on, iterate on -> place, run, observe, repeat. (workflow 2)
47
+ *
48
+ * `iterate` without `autoRunCode` is inert: nothing ran, so there is nothing
49
+ * to observe. That falls out of the data (no runs) rather than a guard.
50
+ *
51
+ * Rounds end when nothing ran (the agent answered without code, so it is
52
+ * done), or on Stop. The server enforces its own EXECUTE_MAX_ROUNDS: at the
53
+ * cap it still answers, explaining the last run with any code stripped, and
54
+ * that code-free answer ends the loop here. `maxRounds` is therefore only a
55
+ * safety net against a server that keeps sending code, checked AFTER the
56
+ * observe so the server's explanation turn is always requested. Every round's
57
+ * message is kept: a chain that took three steps should read as three steps,
58
+ * not as the last one.
59
+ */
60
+ export declare function runChatLoop(prompt: string, history: ChatMessage[], deps: LoopDeps, options: LoopOptions): Promise<string>;
package/lib/aiLoop.js ADDED
@@ -0,0 +1,85 @@
1
+ export const STOPPED = 'Stopped.';
2
+ /**
3
+ * One prompt, start to finish, under the two switches.
4
+ *
5
+ * The loop lives here rather than on the server because the CLIENT owns the
6
+ * kernel: only it can run a cell and read what came back. The server sees one
7
+ * turn at a time through `/api/chat/observe`.
8
+ *
9
+ * autoRunCode off -> place blocks, stop. (workflow 3)
10
+ * autoRunCode on, iterate off -> place, run, stop. (workflow 1)
11
+ * autoRunCode on, iterate on -> place, run, observe, repeat. (workflow 2)
12
+ *
13
+ * `iterate` without `autoRunCode` is inert: nothing ran, so there is nothing
14
+ * to observe. That falls out of the data (no runs) rather than a guard.
15
+ *
16
+ * Rounds end when nothing ran (the agent answered without code, so it is
17
+ * done), or on Stop. The server enforces its own EXECUTE_MAX_ROUNDS: at the
18
+ * cap it still answers, explaining the last run with any code stripped, and
19
+ * that code-free answer ends the loop here. `maxRounds` is therefore only a
20
+ * safety net against a server that keeps sending code, checked AFTER the
21
+ * observe so the server's explanation turn is always requested. Every round's
22
+ * message is kept: a chain that took three steps should read as three steps,
23
+ * not as the last one.
24
+ */
25
+ export async function runChatLoop(prompt, history, deps, options) {
26
+ const { signal } = options;
27
+ const messages = [];
28
+ const runs = [];
29
+ let response;
30
+ let replaceCodeCell;
31
+ try {
32
+ const first = await deps.route(prompt, history, signal);
33
+ response = first.response;
34
+ replaceCodeCell = first.hasCodeCellSelected;
35
+ }
36
+ catch (err) {
37
+ if (signal.aborted) {
38
+ return STOPPED;
39
+ }
40
+ throw err;
41
+ }
42
+ if (signal.aborted) {
43
+ return STOPPED;
44
+ }
45
+ for (let round = 1;; round++) {
46
+ const placed = await deps.place(response, {
47
+ replaceCodeCell,
48
+ autoRun: options.autoRunCode,
49
+ signal
50
+ });
51
+ messages.push(placed.message);
52
+ runs.push(...placed.runs);
53
+ replaceCodeCell = false;
54
+ if (signal.aborted) {
55
+ messages.push(STOPPED);
56
+ break;
57
+ }
58
+ if (!options.iterate || placed.runs.length === 0 || placed.halted) {
59
+ // Nothing ran this round, or nothing could, so there is nothing to
60
+ // observe.
61
+ break;
62
+ }
63
+ if (round > options.maxRounds) {
64
+ break;
65
+ }
66
+ try {
67
+ response = await deps.observe(prompt, history, runs, signal);
68
+ }
69
+ catch (err) {
70
+ // Stop pressed while the request was in flight. What was already placed
71
+ // and explained stays in the chat; only the next step is dropped.
72
+ if (signal.aborted) {
73
+ messages.push(STOPPED);
74
+ break;
75
+ }
76
+ throw err;
77
+ }
78
+ if (signal.aborted) {
79
+ messages.push(STOPPED);
80
+ break;
81
+ }
82
+ }
83
+ return messages.filter(m => m.trim().length > 0).join('\n\n');
84
+ }
85
+ //# sourceMappingURL=aiLoop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiLoop.js","sourceRoot":"","sources":["../src/aiLoop.ts"],"names":[],"mappings":"AA+CA,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,OAAsB,EACtB,IAAc,EACd,OAAoB;IAEpB,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAkB,EAAE,CAAC;IAE/B,IAAI,QAAyB,CAAC;IAC9B,IAAI,eAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACxD,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC1B,eAAe,GAAG,KAAK,CAAC,mBAAmB,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,GAAI,KAAK,EAAE,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;YACxC,eAAe;YACf,OAAO,EAAE,OAAO,CAAC,WAAW;YAC5B,MAAM;SACP,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,eAAe,GAAG,KAAK,CAAC;QAExB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,MAAM;QACR,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClE,mEAAmE;YACnE,WAAW;YACX,MAAM;QACR,CAAC;QACD,IAAI,KAAK,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;YAC9B,MAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,wEAAwE;YACxE,kEAAkE;YAClE,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,MAAM;YACR,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
2
+ import type { Progress } from './progress';
3
+ import type { ObservedRun } from './notebookRun';
4
+ import type { INotebookSnapshot } from './notebookContext';
5
+ /** One thing the backend asks us to place in the notebook. */
6
+ export interface Block {
7
+ type: 'code' | 'markdown';
8
+ content: string;
9
+ }
10
+ /**
11
+ * What `/api/chat` answers with: one message, plus anything to place.
12
+ *
13
+ * This replaced a discriminated union of text/code/markdown responses
14
+ * (OCE-173). The union was exclusive, so the backend could not send a markdown
15
+ * table AND the query that produced it — it had to drop one. `message` is what
16
+ * goes in the chat window; `blocks` is what goes in the notebook, in order.
17
+ *
18
+ * `message` is always present now. On the old markdown variant it was optional,
19
+ * which is why the handoff used to fall back to 'Added markdown to notebook.'
20
+ */
21
+ export interface OceanumResponse {
22
+ message: string;
23
+ blocks: Block[];
24
+ }
25
+ export interface ChatMessage {
26
+ role: 'user' | 'assistant';
27
+ content: string;
28
+ }
29
+ export interface RouteResult {
30
+ response: OceanumResponse;
31
+ hasCodeCellSelected: boolean;
32
+ }
33
+ export declare class ChatRouterError extends Error {
34
+ readonly statusCode?: number;
35
+ constructor(message: string, statusCode?: number);
36
+ }
37
+ export declare class ChatRouter {
38
+ private _settings;
39
+ private _notebook;
40
+ /**
41
+ * @param _notebook What the conversation's notebook holds -- the one its
42
+ * answers are placed in -- or null if there is nothing to send. Asked on
43
+ * every request, so it follows the conversation rather than whichever tab
44
+ * happens to be active.
45
+ */
46
+ constructor(_settings: ISettingRegistry.ISettings, _notebook: () => Promise<INotebookSnapshot | null>);
47
+ route(prompt: string, chatHistory?: ChatMessage[], signal?: AbortSignal, onProgress?: (progress: Progress) => void): Promise<RouteResult>;
48
+ /**
49
+ * The notebook half of the execute loop: the code ran in the user's own
50
+ * kernel, here is what happened, what next? Same context as `route`, plus
51
+ * every run so far, so the agent sees the whole chain it is continuing.
52
+ */
53
+ observe(prompt: string, chatHistory: ChatMessage[], runs: ObservedRun[], signal?: AbortSignal, onProgress?: (progress: Progress) => void): Promise<OceanumResponse>;
54
+ private _gather;
55
+ private _send;
56
+ /**
57
+ * Read the streamed form, reporting progress, and return the final answer.
58
+ *
59
+ * The `done` event is authoritative: the events before it are for the user
60
+ * to look at, not for the client to reassemble. Anything else and a client
61
+ * that missed a frame would silently answer with less than the agent said.
62
+ */
63
+ private _drain;
64
+ }
@@ -0,0 +1,225 @@
1
+ import { OCEANUM_AI_BACKEND_URL } from './constants';
2
+ import { readSse } from './sse';
3
+ import { formatNotebookCells } from './notebookContext';
4
+ export class ChatRouterError extends Error {
5
+ constructor(message, statusCode) {
6
+ super(message);
7
+ this.statusCode = statusCode;
8
+ this.name = 'ChatRouterError';
9
+ }
10
+ }
11
+ /**
12
+ * Check the body is the shape we expect before anything reads it.
13
+ *
14
+ * A bare `as OceanumResponse` cast used to be enough because the shape had not
15
+ * changed since the extension was written. It has now (OCE-173), and an
16
+ * extension pinned to one contract will meet a backend serving the other during
17
+ * any deploy where the two are not released together: the old body has no
18
+ * `blocks`, so `response.blocks.length` throws deep inside the handoff and the
19
+ * user sees nothing happen. One check turns that into the error the chat window
20
+ * already knows how to show.
21
+ */
22
+ function asOceanumResponse(data) {
23
+ const body = data;
24
+ if (!body ||
25
+ typeof body.message !== 'string' ||
26
+ !Array.isArray(body.blocks)) {
27
+ throw new ChatRouterError('Unexpected response from the AI backend. It may be running an ' +
28
+ 'incompatible version of the chat API.');
29
+ }
30
+ return { message: body.message, blocks: body.blocks };
31
+ }
32
+ export class ChatRouter {
33
+ /**
34
+ * @param _notebook What the conversation's notebook holds -- the one its
35
+ * answers are placed in -- or null if there is nothing to send. Asked on
36
+ * every request, so it follows the conversation rather than whichever tab
37
+ * happens to be active.
38
+ */
39
+ constructor(_settings, _notebook) {
40
+ this._settings = _settings;
41
+ this._notebook = _notebook;
42
+ }
43
+ async route(prompt, chatHistory = [], signal, onProgress) {
44
+ const { payload, isCodeCell, context } = await this._gather(prompt, chatHistory);
45
+ const data = await this._send('/api/chat', payload, signal, onProgress);
46
+ return {
47
+ response: asOceanumResponse(data),
48
+ hasCodeCellSelected: isCodeCell && context.trim().length > 0
49
+ };
50
+ }
51
+ /**
52
+ * The notebook half of the execute loop: the code ran in the user's own
53
+ * kernel, here is what happened, what next? Same context as `route`, plus
54
+ * every run so far, so the agent sees the whole chain it is continuing.
55
+ */
56
+ async observe(prompt, chatHistory, runs, signal, onProgress) {
57
+ const { payload } = await this._gather(prompt, chatHistory);
58
+ const data = await this._send('/api/chat/observe', { ...payload, runs }, signal, onProgress);
59
+ return asOceanumResponse(data);
60
+ }
61
+ async _gather(prompt, chatHistory) {
62
+ let context = '';
63
+ let isCodeCell = false;
64
+ let notebookCells = [];
65
+ try {
66
+ const snapshot = await this._notebook();
67
+ if (snapshot) {
68
+ notebookCells = formatNotebookCells(snapshot.cells);
69
+ // Answers are placed in this same notebook, so its selected cell is
70
+ // the one a code answer may replace.
71
+ if (snapshot.selected) {
72
+ context = snapshot.selected.source;
73
+ isCodeCell = snapshot.selected.isCode;
74
+ }
75
+ }
76
+ }
77
+ catch (_a) {
78
+ // context is optional — never throw
79
+ }
80
+ // Build payload with all context
81
+ const payload = { prompt };
82
+ if (context) {
83
+ if (isCodeCell) {
84
+ payload.codeContext = context;
85
+ }
86
+ else {
87
+ payload.context = context;
88
+ }
89
+ }
90
+ // Include chat history (excluding the current prompt which is already in 'prompt')
91
+ if (chatHistory.length > 0) {
92
+ payload.chatHistory = chatHistory;
93
+ }
94
+ // Include the conversation's notebook cells
95
+ if (notebookCells.length > 0) {
96
+ payload.notebookCells = notebookCells;
97
+ }
98
+ return { payload, isCodeCell, context };
99
+ }
100
+ async _send(path, payload, signal, onProgress) {
101
+ var _a;
102
+ const token = this._settings.get('datameshToken').composite;
103
+ if (!token) {
104
+ throw new ChatRouterError('Datamesh token not configured. Set your token in Settings → Oceanum.io.');
105
+ }
106
+ let response;
107
+ try {
108
+ response = await fetch(`${OCEANUM_AI_BACKEND_URL}${path}`, {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Content-Type': 'application/json',
112
+ 'X-Datamesh-Token': token,
113
+ // Ask for the streamed form only when someone is listening. The
114
+ // server requires the media type to be NAMED -- a wildcard never
115
+ // selects it -- so omitting this is what keeps the plain JSON path
116
+ // byte-identical to what it always was.
117
+ ...(onProgress ? { Accept: 'text/event-stream' } : {})
118
+ },
119
+ body: JSON.stringify(payload),
120
+ signal
121
+ });
122
+ }
123
+ catch (err) {
124
+ // The user pressed Stop. Not a failure, and not "could not reach"; the
125
+ // caller checks `signal.aborted` and stays quiet.
126
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
127
+ throw err;
128
+ }
129
+ throw new ChatRouterError(`Could not reach Oceanum AI backend at ${OCEANUM_AI_BACKEND_URL}. Is it running?`);
130
+ }
131
+ if (response.status === 401) {
132
+ throw new ChatRouterError('Invalid or expired Datamesh token.', 401);
133
+ }
134
+ if (!response.ok) {
135
+ let detail = '';
136
+ try {
137
+ const body = await response.json();
138
+ detail = (_a = body.detail) !== null && _a !== void 0 ? _a : JSON.stringify(body);
139
+ }
140
+ catch (_b) {
141
+ detail = response.statusText;
142
+ }
143
+ throw new ChatRouterError(`Backend error: ${detail}`, response.status);
144
+ }
145
+ // Stop can also land while the body is still being read; that rejects
146
+ // here, not in the fetch above, and the caller treats it the same way.
147
+ if (onProgress && isEventStream(response)) {
148
+ return this._drain(response, onProgress, signal);
149
+ }
150
+ return response.json();
151
+ }
152
+ /**
153
+ * Read the streamed form, reporting progress, and return the final answer.
154
+ *
155
+ * The `done` event is authoritative: the events before it are for the user
156
+ * to look at, not for the client to reassemble. Anything else and a client
157
+ * that missed a frame would silently answer with less than the agent said.
158
+ */
159
+ async _drain(response, onProgress, signal) {
160
+ var _a;
161
+ if (!response.body) {
162
+ // A proxy buffered the whole response, or this runtime has no
163
+ // ReadableStream. Either way there is nothing to stream; the body is
164
+ // still the answer, so fall back rather than fail.
165
+ return response.json();
166
+ }
167
+ let answer;
168
+ try {
169
+ for await (const event of readSse(response.body, signal)) {
170
+ if (event.event === 'status') {
171
+ const data = parseJson(event.data);
172
+ if (data === null || data === void 0 ? void 0 : data.phase) {
173
+ onProgress(data);
174
+ }
175
+ }
176
+ else if (event.event === 'done') {
177
+ answer = parseJson(event.data);
178
+ }
179
+ else if (event.event === 'error') {
180
+ const data = parseJson(event.data);
181
+ // The status was 200 before this could happen, so the failure travels
182
+ // in the frame and there was nothing for the check above to throw on.
183
+ throw new ChatRouterError(`Backend error: ${(_a = data === null || data === void 0 ? void 0 : data.detail) !== null && _a !== void 0 ? _a : 'the request failed'}`, data === null || data === void 0 ? void 0 : data.status_code);
184
+ }
185
+ }
186
+ }
187
+ catch (err) {
188
+ // Stop fails the read with an AbortError, which the caller reports as
189
+ // Stop, and an `error` event is already the chat's error. Anything else
190
+ // is the connection going away mid-answer -- a redeploy, the network --
191
+ // which fetch reports as a bare TypeError ("terminated"). Left as it
192
+ // was, it reached the command as unexpected and the chat showed nothing.
193
+ if ((signal === null || signal === void 0 ? void 0 : signal.aborted) || err instanceof ChatRouterError) {
194
+ throw err;
195
+ }
196
+ throw new ChatRouterError('The connection to Oceanum AI was lost before the response was complete.');
197
+ }
198
+ if (answer === undefined) {
199
+ // The stream ended without its terminal event -- a dropped connection,
200
+ // or a proxy closing a response that looked idle. Treated as a failure
201
+ // rather than a partial answer, which is indistinguishable from a
202
+ // complete one once it reaches the chat.
203
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
204
+ throw new DOMException('Aborted', 'AbortError');
205
+ }
206
+ throw new ChatRouterError('The response ended before it was complete.');
207
+ }
208
+ return answer;
209
+ }
210
+ }
211
+ function isEventStream(response) {
212
+ var _a, _b;
213
+ return ((_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('text/event-stream')) !== null && _b !== void 0 ? _b : false);
214
+ }
215
+ /** Parse one event's data, returning null rather than throwing: a malformed
216
+ * frame should cost a progress update, not the answer. */
217
+ function parseJson(data) {
218
+ try {
219
+ return JSON.parse(data);
220
+ }
221
+ catch (_a) {
222
+ return null;
223
+ }
224
+ }
225
+ //# sourceMappingURL=chatRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatRouter.js","sourceRoot":"","sources":["../src/chatRouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Build-time constants for Oceanum AI integration.
3
+ * These values are compiled into the extension.
4
+ */
5
+ /** URL of the Oceanum AI backend service */
6
+ export declare const OCEANUM_AI_BACKEND_URL = "https://ai.oceanum.io";
7
+ /**
8
+ * Safety net on observe requests per prompt. The server's EXECUTE_MAX_ROUNDS
9
+ * is what actually caps the chain (at its cap it answers without code, which
10
+ * ends the loop); this only stops a client talking to a server that keeps
11
+ * sending code, and is checked after each observe so the server's explanation
12
+ * turn is always requested.
13
+ */
14
+ export declare const MAX_OBSERVE_ROUNDS = 8;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Build-time constants for Oceanum AI integration.
3
+ * These values are compiled into the extension.
4
+ */
5
+ /** URL of the Oceanum AI backend service */
6
+ export const OCEANUM_AI_BACKEND_URL = 'https://ai.oceanum.io';
7
+ /**
8
+ * Safety net on observe requests per prompt. The server's EXECUTE_MAX_ROUNDS
9
+ * is what actually caps the chain (at its cap it answers without code, which
10
+ * ends the loop); this only stops a client talking to a server that keeps
11
+ * sending code, and is checked after each observe so the server's explanation
12
+ * turn is always requested.
13
+ */
14
+ export const MAX_OBSERVE_ROUNDS = 8;
15
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,4CAA4C;AAC5C,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { NotebookPanel } from '@jupyterlab/notebook';
2
+ /**
3
+ * What ConversationPin needs from JupyterLab -- passed in, so the pin can be
4
+ * tested without it. Both `create` and `reopen` must resolve only once the
5
+ * notebook's contents have loaded: loading replaces every cell, so anything
6
+ * placed before then is wiped.
7
+ */
8
+ export interface INotebookHost {
9
+ /** The notebook in the active main-area tab, or null if that is not one. */
10
+ activeTab(): NotebookPanel | null;
11
+ /** A new notebook, made the active tab. Null if none can be created. */
12
+ create(): Promise<NotebookPanel | null>;
13
+ /** The notebook at `path`, opened again as a tab. Null if it cannot be. */
14
+ reopen(path: string): Promise<NotebookPanel | null>;
15
+ /** Bring `panel`'s tab to the front. */
16
+ activate(panel: NotebookPanel): void;
17
+ }
18
+ /**
19
+ * The notebook the current conversation lives in: what the agent is shown,
20
+ * and where its answers go.
21
+ *
22
+ * Pinned when the conversation starts -- New chat, or the first message after
23
+ * the panel opens -- to the notebook in the ACTIVE TAB or, when that tab is
24
+ * not a notebook, to a new notebook made the active tab. Kept for the whole
25
+ * conversation: switching tabs does not move it, a rename is followed, and a
26
+ * pinned notebook that has been closed is opened again when an answer has
27
+ * cells to place in it. The active tab rather than the tracker's current
28
+ * notebook, which is the LAST-FOCUSED notebook even with a terminal or a text
29
+ * file in front.
30
+ */
31
+ export declare class ConversationPin {
32
+ private readonly _host;
33
+ private _panel;
34
+ private _path;
35
+ private _started;
36
+ private _queue;
37
+ constructor(_host: INotebookHost);
38
+ /** Start a new conversation. Resolves to its notebook's name. */
39
+ start(): Promise<string | null>;
40
+ /**
41
+ * Start the conversation if nothing has, and report its notebook's name
42
+ * either way.
43
+ */
44
+ ensure(): Promise<string | null>;
45
+ /**
46
+ * The conversation's notebook if it is open, starting the conversation if
47
+ * nothing has -- but NOT opening a closed one again: that is for placing an
48
+ * answer, not for asking a question. See path() for reading a closed one.
49
+ */
50
+ current(): Promise<NotebookPanel | null>;
51
+ /**
52
+ * The conversation's notebook, opened again if it has been closed. When it
53
+ * cannot be -- deleted, or gone some other way -- a new notebook takes its
54
+ * place, so the conversation always has one. Null only if none can be made.
55
+ */
56
+ notebook(): Promise<NotebookPanel | null>;
57
+ /** The conversation's notebook, brought to the front: see notebook(). */
58
+ show(): Promise<NotebookPanel | null>;
59
+ /** Where the notebook's file is -- its last known place while it is closed. */
60
+ path(): string | null;
61
+ /** The notebook's name -- its last known one while it is closed. */
62
+ name(): string | null;
63
+ private _live;
64
+ private _startNow;
65
+ private _pin;
66
+ private _onPathChanged;
67
+ private _serial;
68
+ }