This commit is contained in:
@@ -21,6 +21,12 @@ export function AskSubtasksForm({ config, onChange }: SectionFormProps) {
|
||||
<FieldInput type="number" value={subtasks.maxDepth ?? ''} onChange={v => onChange('subtasks.maxDepth', v ? Number(v) : undefined)} />
|
||||
<HelpText>サブタスクのネスト最大深度</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Subtasks: Max Per Parent</FieldLabel>
|
||||
<FieldInput type="number" value={subtasks.maxPerParent ?? ''} onChange={v => onChange('subtasks.maxPerParent', v ? Number(v) : undefined)} />
|
||||
<HelpText>1 つの親ジョブが spawn できるサブタスクの最大数。デフォルト: 10</HelpText>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import { ReflectionForm } from './ReflectionForm';
|
||||
import { McpForm } from './McpForm';
|
||||
import { SshForm } from './SshForm';
|
||||
import { GatewayServerForm } from './GatewayServerForm';
|
||||
import { NotesForm } from './NotesForm';
|
||||
import { PushNotificationsForm } from './PushNotificationsForm';
|
||||
|
||||
import { useAuthState } from '../../App';
|
||||
|
||||
@@ -185,6 +187,8 @@ function ConfigFormInner({ section }: ConfigFormProps) {
|
||||
case 'context': return <ContextForm {...formProps} />;
|
||||
case 'safety': return <SafetyForm {...formProps} />;
|
||||
case 'reflection': return <ReflectionForm {...formProps} />;
|
||||
case 'notes': return <NotesForm {...formProps} />;
|
||||
case 'push-notifications': return <PushNotificationsForm {...formProps} />;
|
||||
|
||||
// ── Tools sub-sections — Step 9 split the legacy grab-bag
|
||||
// ToolsForm into focused per-category forms. Each binds to the
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { HelpText } from './HelpText';
|
||||
import { FieldLabel, FieldInput } from './formUtils';
|
||||
import type { SectionFormProps } from './types';
|
||||
|
||||
/**
|
||||
* Shared Knowledge Notes injection budget (`notes.inject.*`). Controls how much
|
||||
* of a subscribed note is injected into the agent's context per job.
|
||||
*/
|
||||
export function NotesForm({ config, onChange }: SectionFormProps) {
|
||||
const inject = config.notes?.inject ?? {};
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<h2 className="text-base font-semibold text-slate-800">Notes Injection</h2>
|
||||
<p className="text-[13px] text-slate-500">
|
||||
購読済みの共有ナレッジノートを、ジョブ実行時にエージェントの context へ注入する際の予算。
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Per-Note Max (KB)</FieldLabel>
|
||||
<FieldInput type="number" value={inject.perNoteMaxKb ?? ''}
|
||||
onChange={v => onChange('notes.inject.perNoteMaxKb', v ? Number(v) : undefined)} />
|
||||
<HelpText>1 ノートあたり注入する最大サイズ。デフォルト: 8 KB</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Total Max (KB)</FieldLabel>
|
||||
<FieldInput type="number" value={inject.totalMaxKb ?? ''}
|
||||
onChange={v => onChange('notes.inject.totalMaxKb', v ? Number(v) : undefined)} />
|
||||
<HelpText>全ノート合算で注入する最大サイズ。デフォルト: 32 KB</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Over-Budget Strategy</FieldLabel>
|
||||
<select
|
||||
value={inject.overBudgetStrategy ?? 'skip_remaining'}
|
||||
onChange={e => onChange('notes.inject.overBudgetStrategy', e.target.value)}
|
||||
className="w-full h-8 px-2 text-[13px] border border-hairline rounded-md bg-canvas focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none transition-shadow"
|
||||
>
|
||||
<option value="skip_remaining">skip_remaining(予算超過後のノートは注入しない)</option>
|
||||
<option value="truncate_last">truncate_last(最後のノートを途中で切り詰める)</option>
|
||||
<option value="degrade_to_search">degrade_to_search(注入せず検索ツールに委ねる)</option>
|
||||
</select>
|
||||
<HelpText>合計予算を超えたときの挙動。デフォルト: skip_remaining</HelpText>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { HelpText } from './HelpText';
|
||||
import { FieldLabel, FieldInput } from './formUtils';
|
||||
import type { SectionFormProps } from './types';
|
||||
|
||||
/**
|
||||
* Server-side Web Push (V2) config (`notifications.push.*`): VAPID identity +
|
||||
* delivery tuning. This is the ADMIN counterpart to the per-user browser
|
||||
* subscription UI in NotificationsForm (which only manages this device's
|
||||
* subscription, not whether the server feature is enabled at all).
|
||||
*/
|
||||
export function PushNotificationsForm({ config, onChange }: SectionFormProps) {
|
||||
const push = config.notifications?.push ?? {};
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<h2 className="text-base font-semibold text-slate-800">Web Push (Server)</h2>
|
||||
<p className="text-[13px] text-slate-500">
|
||||
ブラウザ通知 V2(Web Push)のサーバ設定。HTTPS ホスティング必須(iOS は PWA インストールも)。
|
||||
各ユーザーの購読操作は 🔔 Notifications タブで行う。
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={push.enabled === true}
|
||||
onChange={e => onChange('notifications.push.enabled', e.target.checked)}
|
||||
className="rounded"
|
||||
/>
|
||||
Web Push を有効化
|
||||
</label>
|
||||
<HelpText>マスタースイッチ。デフォルト: 無効(オペレーターが明示的に opt-in)</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>VAPID Subject</FieldLabel>
|
||||
<FieldInput value={push.vapidSubject ?? ''} placeholder="https://example.com/"
|
||||
onChange={v => onChange('notifications.push.vapidSubject', v || undefined)} />
|
||||
<HelpText>RFC 8292 の VAPID subject。汎用 mailto: より運用 URL を推奨</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>VAPID Current Key Path</FieldLabel>
|
||||
<FieldInput value={push.vapidCurrentPath ?? ''} placeholder="./data/secrets/vapid.json"
|
||||
onChange={v => onChange('notifications.push.vapidCurrentPath', v || undefined)} />
|
||||
<HelpText>現行 VAPID 鍵ペアファイルのパス(未生成なら mode 0600 で自動生成)</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>VAPID History Dir</FieldLabel>
|
||||
<FieldInput value={push.vapidHistoryDir ?? ''} placeholder="./data/secrets/vapid-history"
|
||||
onChange={v => onChange('notifications.push.vapidHistoryDir', v || undefined)} />
|
||||
<HelpText>失効した VAPID 鍵を退避するディレクトリ</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Payload Max Bytes</FieldLabel>
|
||||
<FieldInput type="number" value={push.payloadMaxBytes ?? ''}
|
||||
onChange={v => onChange('notifications.push.payloadMaxBytes', v ? Number(v) : undefined)} />
|
||||
<HelpText>暗号化前のプッシュペイロード最大バイト数(上限 4096)。デフォルト: 3072</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Queue Concurrency</FieldLabel>
|
||||
<FieldInput type="number" value={push.queueConcurrency ?? ''}
|
||||
onChange={v => onChange('notifications.push.queueConcurrency', v ? Number(v) : undefined)} />
|
||||
<HelpText>キューからの同時送信数。デフォルト: 8</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Per-Send Timeout (ms)</FieldLabel>
|
||||
<FieldInput type="number" value={push.perSendTimeoutMs ?? ''}
|
||||
onChange={v => onChange('notifications.push.perSendTimeoutMs', v ? Number(v) : undefined)} />
|
||||
<HelpText>1 送信あたりのタイムアウト(ミリ秒)。デフォルト: 10000</HelpText>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -38,6 +38,55 @@ export function SafetyForm({ config, onChange }: SectionFormProps) {
|
||||
<HelpText>送信前に prompt がコンテキスト上限の何割を占めたら自動圧縮するか(0.5〜0.95、デフォルト: 0.8)</HelpText>
|
||||
</div>
|
||||
|
||||
<h3 className="text-sm font-medium text-slate-600 mt-4 pt-3 border-t border-slate-200">Bash サンドボックス</h3>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Bash Sandbox Mode</FieldLabel>
|
||||
<select
|
||||
value={safety.bashSandbox ?? 'auto'}
|
||||
onChange={e => onChange('safety.bashSandbox', e.target.value)}
|
||||
className="w-full h-8 px-2 text-[13px] border border-hairline rounded-md bg-canvas focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none transition-shadow"
|
||||
>
|
||||
<option value="auto">auto(bwrap があれば sandboxed、無ければ hardened-whitelist)</option>
|
||||
<option value="always">always(sandboxed を強制・bwrap 不在なら起動時 fail)</option>
|
||||
<option value="off">off(素 exec・後方互換/非推奨)</option>
|
||||
</select>
|
||||
<HelpText>Bash ツールのサンドボックス機構。デフォルト: auto</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={safety.bashUnrestricted === true}
|
||||
onChange={e => onChange('safety.bashUnrestricted', e.target.checked)}
|
||||
className="rounded"
|
||||
/>
|
||||
コマンドのホワイトリストを解除(無制限 Bash)
|
||||
</label>
|
||||
<HelpText>bwrap サンドボックス下で任意コマンドを許可する。ワークスペース(rw)とシステムディレクトリ(ro)のみ bind-mount。bwrap の user-namespace 対応が必要。デフォルト: 無効(セキュリティ上、変更は慎重に)</HelpText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={safety.bashAllowNetwork === true}
|
||||
onChange={e => onChange('safety.bashAllowNetwork', e.target.checked)}
|
||||
className="rounded"
|
||||
/>
|
||||
サンドボックスでネットワークを許可(bash / python / npm がネット接続可)
|
||||
</label>
|
||||
<HelpText>
|
||||
<span className="text-red-700 dark:text-red-300 font-medium">⚠ セキュリティ警告:</span>{' '}
|
||||
通常はサンドボックス内の bash・python・npm はネット遮断(<code>--unshare-net</code>)されます。
|
||||
有効にすると pip / npm install / curl 等が使えるようになりますが、
|
||||
<strong>サンドボックスの隔離が弱まり、任意のデータ送信(情報漏えい)や内部ネットワーク・メタデータエンドポイントへの到達(SSRF)が可能になります</strong>。
|
||||
信頼できる環境でのみ有効化してください。デフォルト: 無効。
|
||||
(Bash Sandbox Mode が <code>off</code> や bwrap 不在時は元々ネット遮断が無いため、この設定は sandboxed 時のみ効きます)
|
||||
</HelpText>
|
||||
</div>
|
||||
|
||||
<h3 className="text-sm font-medium text-slate-600 mt-4 pt-3 border-t border-slate-200">History Summarization</h3>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -34,6 +34,7 @@ const CONFIG_GROUPS = [
|
||||
{ id: 'branding', label: 'Branding' },
|
||||
{ id: 'paths-storage', label: 'Paths & Storage' },
|
||||
{ id: 'execution', label: 'Execution' },
|
||||
{ id: 'push-notifications', label: 'Web Push (Server)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -56,6 +57,7 @@ const CONFIG_GROUPS = [
|
||||
{ id: 'context', label: 'Context' },
|
||||
{ id: 'safety', label: 'Safety' },
|
||||
{ id: 'reflection', label: 'Reflection' },
|
||||
{ id: 'notes', label: 'Notes Injection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ const SETTINGS_SECTIONS = [
|
||||
'branding',
|
||||
'paths-storage',
|
||||
'execution',
|
||||
'push-notifications',
|
||||
// LLM group
|
||||
'llm-workers',
|
||||
'gateway-server',
|
||||
@@ -21,6 +22,7 @@ const SETTINGS_SECTIONS = [
|
||||
'context',
|
||||
'safety',
|
||||
'reflection',
|
||||
'notes',
|
||||
// Tools group (sub-sections)
|
||||
'tools-web',
|
||||
'tools-browser',
|
||||
|
||||
Reference in New Issue
Block a user