@oar-sh/oar 0.9.25 → 0.9.26

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 (33) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +9 -9
  3. package/package.json +3 -1
  4. package/server/claude-worker/claude-sdk-adapter.mjs +8 -4
  5. package/server/claude-worker/claude-session-process.mjs +4 -0
  6. package/server/claude-worker/claude-turn-publisher.mjs +25 -11
  7. package/server/copilot-worker/copilot-prompt-context.mjs +2 -1
  8. package/server/cursor-worker/cursor-turn-runner.mjs +3 -1
  9. package/server/grok-worker/grok-turn-runner.mjs +3 -1
  10. package/server/public/app/context-usage-view.mjs +2 -2
  11. package/server/public/app/live-activity-cap.mjs +3 -3
  12. package/server/public/app/markdown-math.mjs +471 -0
  13. package/server/public/app/pwa-install.js +25 -4
  14. package/server/public/app/relay-prompt-sanitizer.mjs +3 -0
  15. package/server/public/app/relay-restart-ui.js +2 -2
  16. package/server/public/app/remote-relays-settings.js +33 -6
  17. package/server/public/app/router.js +49 -237
  18. package/server/public/app/settings-tabs.js +10 -1
  19. package/server/public/app/store.js +2 -0
  20. package/server/public/app/stream-view-preference.mjs +1 -1
  21. package/server/public/app/thoughts-preference.mjs +1 -1
  22. package/server/public/app/tmux-inspector-view.js +29 -16
  23. package/server/public/index.html +134 -116
  24. package/server/routes/relay-board-routes.mjs +46 -1
  25. package/server/routes/remote-relay-routes.mjs +1 -1
  26. package/server/server-runtime.mjs +2 -0
  27. package/server/services/relay-prompt-sanitizer.mjs +7 -0
  28. package/server/services/relay-restart-queue.mjs +1 -1
  29. package/server/services/remote-relay-dispatcher.mjs +23 -1
  30. package/server/services/remote-relay-registry-service.mjs +7 -1
  31. package/server/services/session-worker-stop-service.mjs +50 -18
  32. package/shared/math-instructions.mjs +30 -0
  33. package/server/public/app/math-dollars.mjs +0 -89
@@ -0,0 +1,30 @@
1
+ // How agents write formulas in their reply messages, taught once per provider
2
+ // through the same four seams as shared/media-embed-instructions.mjs: Claude
3
+ // (system-prompt append), Copilot (relay tool guidance document), Cursor and
4
+ // Grok (once-per-worker prompt prefix). The web app reads a formula from its
5
+ // delimiters only (server/public/app/markdown-math.mjs); a model that writes
6
+ // TeX in bare parentheses or brackets gets its source shown, so every agent
7
+ // is told the notation that renders.
8
+
9
+ export const MATH_INSTRUCTION_HEADING = '## Formulas in replies';
10
+
11
+ export const MATH_INSTRUCTION_TEXT =
12
+ 'To show a formula in a reply, write it in TeX between dollar signs: '
13
+ + '$E = mc^2$ inside a sentence, and $$ ... $$ on lines of its own for a '
14
+ + 'displayed formula. The web app renders both with KaTeX; \\( ... \\) and '
15
+ + '\\[ ... \\] work as well. TeX in bare parentheses or square brackets is '
16
+ + 'not rendered. A dollar amount such as $5 stays text.';
17
+
18
+ export function renderMathInstructionBlock() {
19
+ return `${MATH_INSTRUCTION_HEADING}\n\n${MATH_INSTRUCTION_TEXT}`;
20
+ }
21
+
22
+ /**
23
+ * Append the formula block to a guidance document unless it already carries
24
+ * one (idempotent, like applyMediaEmbedInstructions).
25
+ */
26
+ export function applyMathInstructions(baseInstructions = '') {
27
+ const base = String(baseInstructions || '').trimEnd();
28
+ if (base.includes(MATH_INSTRUCTION_HEADING)) return base;
29
+ return [base, renderMathInstructionBlock()].filter(Boolean).join('\n\n');
30
+ }
@@ -1,89 +0,0 @@
1
- // Which `$` in a run of text delimit inline maths, and which are plain dollar
2
- // signs. Prices are the common case of the second kind: without a rule,
3
- // "$5 for one and $9 for two" reads as one formula running from the first
4
- // sign to the second.
5
- //
6
- // The rule is the one pandoc uses for dollar maths: the opening `$` has a
7
- // non-space character directly after it, the closing `$` is the next dollar
8
- // sign, has a non-space character directly before it and no digit directly
9
- // after it. A `$$` pair is
10
- // display maths and is left alone, and a `$` behind a backslash is escaped.
11
-
12
- const TEX_LOOKING = /[\\^_{}=]/;
13
-
14
- function isEscaped(text, index) {
15
- let backslashes = 0;
16
- for (let i = index - 1; i >= 0 && text[i] === '\\'; i -= 1) backslashes += 1;
17
- return backslashes % 2 === 1;
18
- }
19
-
20
- function isSpace(char) {
21
- return char === undefined || /\s/.test(char);
22
- }
23
-
24
- // A formula holds no dollar sign of its own, so only the next one can close
25
- // it. When that one does not qualify ("$5, and $x^2$": it follows a space),
26
- // the opening sign is a plain dollar and the next one gets its own turn.
27
- function findClosingDollar(text, openIndex) {
28
- let i = openIndex + 1;
29
- while (i < text.length && (text[i] !== '$' || isEscaped(text, i))) i += 1;
30
- if (i >= text.length) return -1;
31
- // Part of a `$$`: not the end of an inline formula.
32
- if (text[i + 1] === '$') return -1;
33
- if (isSpace(text[i - 1])) return -1;
34
- const content = text.slice(openIndex + 1, i);
35
- // "$5/$10": a digit right behind the sign means the sign starts a number.
36
- // Real maths directly followed by a digit ("$x^2$3") is told apart by
37
- // looking like TeX.
38
- if (/\d/.test(text[i + 1] || '') && !TEX_LOOKING.test(content)) return -1;
39
- return i;
40
- }
41
-
42
- /**
43
- * Scan `text` for single-dollar maths.
44
- * @returns {{ spans: Array<[number, number]>, literals: number[] }} `spans`
45
- * are [open, close] index pairs of formulas, `literals` the indexes of `$`
46
- * that are plain dollar signs.
47
- */
48
- export function scanInlineDollars(text) {
49
- const source = String(text ?? '');
50
- const spans = [];
51
- const literals = [];
52
- let index = 0;
53
- while (index < source.length) {
54
- if (source[index] !== '$' || isEscaped(source, index)) {
55
- index += 1;
56
- continue;
57
- }
58
- if (source[index + 1] === '$') {
59
- const close = source.indexOf('$$', index + 2);
60
- index = close < 0 ? index + 2 : close + 2;
61
- continue;
62
- }
63
- const close = isSpace(source[index + 1]) ? -1 : findClosingDollar(source, index);
64
- if (close < 0) {
65
- literals.push(index);
66
- index += 1;
67
- continue;
68
- }
69
- spans.push([index, close]);
70
- index = close + 1;
71
- }
72
- return { spans, literals };
73
- }
74
-
75
- /** `text` cut at its plain dollar signs: `[{ text, literal }]`, in order. */
76
- export function splitAtLiteralDollars(text) {
77
- const source = String(text ?? '');
78
- const { literals } = scanInlineDollars(source);
79
- if (!literals.length) return [{ text: source, literal: false }];
80
- const parts = [];
81
- let from = 0;
82
- for (const index of literals) {
83
- if (index > from) parts.push({ text: source.slice(from, index), literal: false });
84
- parts.push({ text: '$', literal: true });
85
- from = index + 1;
86
- }
87
- if (from < source.length) parts.push({ text: source.slice(from), literal: false });
88
- return parts;
89
- }