@deepstrike/core 0.1.1 → 0.1.3
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/npm/darwin-arm64/deepstrike-core.darwin-arm64.node +0 -0
- package/npm/darwin-arm64/package.json +1 -1
- package/npm/darwin-x64/deepstrike-core.darwin-x64.node +0 -0
- package/npm/darwin-x64/package.json +1 -1
- package/npm/linux-arm64-gnu/deepstrike-core.linux-arm64-gnu.node +0 -0
- package/npm/linux-arm64-gnu/package.json +1 -1
- package/npm/linux-arm64-musl/package.json +1 -1
- package/npm/linux-x64-gnu/deepstrike-core.linux-x64-gnu.node +0 -0
- package/npm/linux-x64-gnu/package.json +1 -1
- package/npm/linux-x64-musl/package.json +1 -1
- package/npm/win32-x64-msvc/deepstrike-core.win32-x64-msvc.node +0 -0
- package/npm/win32-x64-msvc/package.json +1 -1
- package/package.json +6 -6
- package/src/lib.rs +158 -21
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "DeepStrike kernel — pre-built native addon",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
]
|
|
17
17
|
},
|
|
18
18
|
"optionalDependencies": {
|
|
19
|
-
"@deepstrike/core-linux-x64-gnu": "0.1.
|
|
20
|
-
"@deepstrike/core-linux-arm64-gnu": "0.1.
|
|
21
|
-
"@deepstrike/core-darwin-x64": "0.1.
|
|
22
|
-
"@deepstrike/core-darwin-arm64": "0.1.
|
|
23
|
-
"@deepstrike/core-win32-x64-msvc": "0.1.
|
|
19
|
+
"@deepstrike/core-linux-x64-gnu": "0.1.3",
|
|
20
|
+
"@deepstrike/core-linux-arm64-gnu": "0.1.3",
|
|
21
|
+
"@deepstrike/core-darwin-x64": "0.1.3",
|
|
22
|
+
"@deepstrike/core-darwin-arm64": "0.1.3",
|
|
23
|
+
"@deepstrike/core-win32-x64-msvc": "0.1.3"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/src/lib.rs
CHANGED
|
@@ -43,7 +43,10 @@ use compact_str::CompactString;
|
|
|
43
43
|
|
|
44
44
|
use deepstrike_core::context::manager::ContextManager;
|
|
45
45
|
use deepstrike_core::context::pressure::PressureAction;
|
|
46
|
+
use deepstrike_core::governance::permission::{PermissionAction, PermissionRule};
|
|
46
47
|
use deepstrike_core::governance::pipeline::GovernancePipeline as RustGovernancePipeline;
|
|
48
|
+
use deepstrike_core::types::agent::AgentIdentity;
|
|
49
|
+
use deepstrike_core::types::policy::GovernanceVerdict as RustGovernanceVerdict;
|
|
47
50
|
use deepstrike_core::harness::eval_pipeline::{
|
|
48
51
|
EvalAction as RustEvalAction, EvalEvent as RustEvalEvent, EvalPolicy as RustEvalPolicy,
|
|
49
52
|
EvalPipeline as RustEvalPipeline,
|
|
@@ -76,11 +79,30 @@ use deepstrike_core::types::task::RuntimeTask as RustRuntimeTask;
|
|
|
76
79
|
|
|
77
80
|
// ────────────────────────────────────── POD types (plain JS objects) ──────────────────────────────────────
|
|
78
81
|
|
|
82
|
+
#[napi(object)]
|
|
83
|
+
#[derive(Clone)]
|
|
84
|
+
pub struct ContentPartObj {
|
|
85
|
+
/// `"text"` | `"image"` | `"audio"` | `"tool_result"`
|
|
86
|
+
pub r#type: String,
|
|
87
|
+
pub text: Option<String>,
|
|
88
|
+
pub url: Option<String>,
|
|
89
|
+
pub data: Option<String>,
|
|
90
|
+
pub media_type: Option<String>,
|
|
91
|
+
pub detail: Option<String>,
|
|
92
|
+
pub call_id: Option<String>,
|
|
93
|
+
pub output: Option<String>,
|
|
94
|
+
pub is_error: Option<bool>,
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
#[napi(object)]
|
|
80
98
|
#[derive(Clone)]
|
|
81
99
|
pub struct Message {
|
|
82
100
|
pub role: String,
|
|
101
|
+
/// Plain-text content. When `content_parts` is present, this holds only the
|
|
102
|
+
/// concatenated text segments for backward compatibility.
|
|
83
103
|
pub content: String,
|
|
104
|
+
/// Structured multimodal content parts. When present, takes precedence over `content`.
|
|
105
|
+
pub content_parts: Option<Vec<ContentPartObj>>,
|
|
84
106
|
pub token_count: Option<u32>,
|
|
85
107
|
pub tool_calls: Vec<ToolCall>,
|
|
86
108
|
}
|
|
@@ -288,6 +310,51 @@ fn role_to_str(role: Role) -> &'static str {
|
|
|
288
310
|
}
|
|
289
311
|
}
|
|
290
312
|
|
|
313
|
+
fn content_part_to_rust(p: ContentPartObj) -> ContentPart {
|
|
314
|
+
match p.r#type.as_str() {
|
|
315
|
+
"image" => ContentPart::Image {
|
|
316
|
+
url: p.url,
|
|
317
|
+
data: p.data,
|
|
318
|
+
media_type: p.media_type,
|
|
319
|
+
detail: p.detail,
|
|
320
|
+
},
|
|
321
|
+
"audio" => ContentPart::Audio {
|
|
322
|
+
data: p.data.unwrap_or_default(),
|
|
323
|
+
media_type: p.media_type.unwrap_or_else(|| "audio/wav".into()),
|
|
324
|
+
},
|
|
325
|
+
"tool_result" => ContentPart::ToolResult {
|
|
326
|
+
call_id: CompactString::new(&p.call_id.unwrap_or_default()),
|
|
327
|
+
output: p.output.unwrap_or_default(),
|
|
328
|
+
is_error: p.is_error.unwrap_or(false),
|
|
329
|
+
},
|
|
330
|
+
_ => ContentPart::Text { text: p.text.unwrap_or_default() },
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
fn content_part_from_rust(p: &ContentPart) -> ContentPartObj {
|
|
335
|
+
match p {
|
|
336
|
+
ContentPart::Text { text } => ContentPartObj {
|
|
337
|
+
r#type: "text".into(), text: Some(text.clone()),
|
|
338
|
+
url: None, data: None, media_type: None, detail: None,
|
|
339
|
+
call_id: None, output: None, is_error: None,
|
|
340
|
+
},
|
|
341
|
+
ContentPart::Image { url, data, media_type, detail } => ContentPartObj {
|
|
342
|
+
r#type: "image".into(), text: None,
|
|
343
|
+
url: url.clone(), data: data.clone(), media_type: media_type.clone(), detail: detail.clone(),
|
|
344
|
+
call_id: None, output: None, is_error: None,
|
|
345
|
+
},
|
|
346
|
+
ContentPart::Audio { data, media_type } => ContentPartObj {
|
|
347
|
+
r#type: "audio".into(), text: None, url: None,
|
|
348
|
+
data: Some(data.clone()), media_type: Some(media_type.clone()), detail: None,
|
|
349
|
+
call_id: None, output: None, is_error: None,
|
|
350
|
+
},
|
|
351
|
+
ContentPart::ToolResult { call_id, output, is_error } => ContentPartObj {
|
|
352
|
+
r#type: "tool_result".into(), text: None, url: None, data: None, media_type: None, detail: None,
|
|
353
|
+
call_id: Some(call_id.to_string()), output: Some(output.clone()), is_error: Some(*is_error),
|
|
354
|
+
},
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
291
358
|
fn message_to_rust(m: Message) -> Result<RustMessage> {
|
|
292
359
|
let role = role_str_to_rust(&m.role)?;
|
|
293
360
|
let tool_calls: Vec<RustToolCall> = m
|
|
@@ -295,32 +362,29 @@ fn message_to_rust(m: Message) -> Result<RustMessage> {
|
|
|
295
362
|
.into_iter()
|
|
296
363
|
.map(tool_call_to_rust)
|
|
297
364
|
.collect::<Result<_>>()?;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
})
|
|
365
|
+
let content = match m.content_parts {
|
|
366
|
+
Some(parts) if !parts.is_empty() => Content::Parts(parts.into_iter().map(content_part_to_rust).collect()),
|
|
367
|
+
_ => Content::Text(m.content),
|
|
368
|
+
};
|
|
369
|
+
Ok(RustMessage { role, content, tool_calls, token_count: m.token_count })
|
|
304
370
|
}
|
|
305
371
|
|
|
306
372
|
fn message_from_rust(m: &RustMessage) -> Message {
|
|
307
|
-
let content = match &m.content {
|
|
308
|
-
Content::Text(s) => s.clone(),
|
|
309
|
-
Content::Parts(parts) =>
|
|
310
|
-
.iter()
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
})
|
|
318
|
-
.collect::<Vec<_>>()
|
|
319
|
-
.join("\n"),
|
|
373
|
+
let (content, content_parts) = match &m.content {
|
|
374
|
+
Content::Text(s) => (s.clone(), None),
|
|
375
|
+
Content::Parts(parts) => {
|
|
376
|
+
let text_only: String = parts.iter().filter_map(|p| match p {
|
|
377
|
+
ContentPart::Text { text } => Some(text.as_str()),
|
|
378
|
+
_ => None,
|
|
379
|
+
}).collect::<Vec<_>>().join("\n");
|
|
380
|
+
let objs: Vec<ContentPartObj> = parts.iter().map(content_part_from_rust).collect();
|
|
381
|
+
(text_only, Some(objs))
|
|
382
|
+
}
|
|
320
383
|
};
|
|
321
384
|
Message {
|
|
322
385
|
role: role_to_str(m.role).to_string(),
|
|
323
386
|
content,
|
|
387
|
+
content_parts,
|
|
324
388
|
token_count: m.token_count,
|
|
325
389
|
tool_calls: m.tool_calls.iter().map(tool_call_from_rust).collect(),
|
|
326
390
|
}
|
|
@@ -683,27 +747,100 @@ impl SignalRouter {
|
|
|
683
747
|
|
|
684
748
|
// ─────────────────────────────────────────── Governance ───────────────────────────────────────────
|
|
685
749
|
|
|
750
|
+
/// JS-friendly governance verdict returned by `Governance.evaluate`.
|
|
751
|
+
#[napi(object)]
|
|
752
|
+
#[derive(Clone)]
|
|
753
|
+
pub struct GovernanceVerdictObj {
|
|
754
|
+
/// `"allow"` | `"deny"` | `"rate_limited"` | `"ask_user"`
|
|
755
|
+
pub kind: String,
|
|
756
|
+
pub reason: Option<String>,
|
|
757
|
+
/// Milliseconds until the tool may be retried. Only set when `kind === "rate_limited"`.
|
|
758
|
+
pub retry_after_ms: Option<f64>,
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
fn verdict_to_js(v: RustGovernanceVerdict) -> GovernanceVerdictObj {
|
|
762
|
+
match v {
|
|
763
|
+
RustGovernanceVerdict::Allow => GovernanceVerdictObj { kind: "allow".into(), reason: None, retry_after_ms: None },
|
|
764
|
+
RustGovernanceVerdict::Deny { reason, .. } => GovernanceVerdictObj { kind: "deny".into(), reason: Some(reason), retry_after_ms: None },
|
|
765
|
+
RustGovernanceVerdict::RateLimited { retry_after_ms } => GovernanceVerdictObj { kind: "rate_limited".into(), reason: None, retry_after_ms: Some(retry_after_ms as f64) },
|
|
766
|
+
RustGovernanceVerdict::AskUser { reason } => GovernanceVerdictObj { kind: "ask_user".into(), reason: Some(reason), retry_after_ms: None },
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
686
770
|
#[napi]
|
|
687
771
|
pub struct Governance {
|
|
688
772
|
inner: RustGovernancePipeline,
|
|
773
|
+
agent_id: String,
|
|
774
|
+
session_id: String,
|
|
689
775
|
}
|
|
690
776
|
|
|
691
777
|
#[napi]
|
|
692
778
|
impl Governance {
|
|
779
|
+
/// Create a governance pipeline.
|
|
780
|
+
/// `defaultAction` controls the fallback when no rule matches: `"allow"` (default) or `"deny"`.
|
|
693
781
|
#[napi(constructor)]
|
|
694
|
-
pub fn new() -> Self {
|
|
695
|
-
|
|
782
|
+
pub fn new(default_action: Option<String>) -> Self {
|
|
783
|
+
let action = match default_action.as_deref() {
|
|
784
|
+
Some("deny") => PermissionAction::Deny,
|
|
785
|
+
Some("ask_user") => PermissionAction::AskUser,
|
|
786
|
+
_ => PermissionAction::Allow,
|
|
787
|
+
};
|
|
788
|
+
Self {
|
|
789
|
+
inner: RustGovernancePipeline::new(action),
|
|
790
|
+
agent_id: "anonymous".into(),
|
|
791
|
+
session_id: "".into(),
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/// Set the agent identity used in governance audit logs.
|
|
796
|
+
#[napi]
|
|
797
|
+
pub fn set_identity(&mut self, agent_id: String, session_id: String) {
|
|
798
|
+
self.agent_id = agent_id;
|
|
799
|
+
self.session_id = session_id;
|
|
696
800
|
}
|
|
697
801
|
|
|
802
|
+
/// Add a permission rule. `pattern` supports globs: `"db.*"`, `"*.delete"`, `"*"`, or exact names.
|
|
803
|
+
/// `action`: `"allow"` | `"deny"` | `"ask_user"`.
|
|
804
|
+
/// Rules are evaluated in insertion order; first match wins.
|
|
805
|
+
#[napi]
|
|
806
|
+
pub fn add_permission_rule(&mut self, pattern: String, action: String) {
|
|
807
|
+
let perm_action = match action.as_str() {
|
|
808
|
+
"deny" => PermissionAction::Deny,
|
|
809
|
+
"ask_user" => PermissionAction::AskUser,
|
|
810
|
+
_ => PermissionAction::Allow,
|
|
811
|
+
};
|
|
812
|
+
self.inner.permission.add_rule(PermissionRule {
|
|
813
|
+
tool_pattern: pattern.into(),
|
|
814
|
+
action: perm_action,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/// Hard-block a tool name (veto stage — cannot be overridden by permission rules).
|
|
698
819
|
#[napi]
|
|
699
820
|
pub fn block_tool(&mut self, name: String) {
|
|
700
821
|
self.inner.veto.block_tool(name);
|
|
701
822
|
}
|
|
702
823
|
|
|
824
|
+
/// Advance the internal clock used by rate limiting and audit.
|
|
703
825
|
#[napi]
|
|
704
826
|
pub fn set_time(&mut self, now_ms: BigInt) {
|
|
705
827
|
self.inner.set_time(now_ms.get_u64().1);
|
|
706
828
|
}
|
|
829
|
+
|
|
830
|
+
/// Evaluate a tool call through the full pipeline (Permission → Veto → RateLimit → Constraint → Audit).
|
|
831
|
+
/// `argsJson`: JSON-encoded tool arguments string.
|
|
832
|
+
#[napi]
|
|
833
|
+
pub fn evaluate(&mut self, tool_name: String, args_json: String) -> Result<GovernanceVerdictObj> {
|
|
834
|
+
let args: serde_json::Value = serde_json::from_str(&args_json)
|
|
835
|
+
.unwrap_or(serde_json::Value::Null);
|
|
836
|
+
let call = RustToolCall {
|
|
837
|
+
id: compact_str::CompactString::new(""),
|
|
838
|
+
name: compact_str::CompactString::new(&tool_name),
|
|
839
|
+
arguments: args,
|
|
840
|
+
};
|
|
841
|
+
let caller = AgentIdentity::new(self.agent_id.as_str(), self.session_id.as_str());
|
|
842
|
+
Ok(verdict_to_js(self.inner.evaluate(&call, &caller)))
|
|
843
|
+
}
|
|
707
844
|
}
|
|
708
845
|
|
|
709
846
|
// ──────────────────────────────── Dream / idle-pipeline POD types ────────────────────────────────
|